Commit 0ded0602 authored by Nico Schottelius's avatar Nico Schottelius

Merge branch 'uid-validation' into 'master'

Minimal README, validate username format on registration, fix field name on login

See merge request ungleich-public/ungleich-user!7
parents de44229a 26916f7d
## ungleich frontend
# Ungleich Account Management Webapp
The frontend pages of ungleich micro services
This service runs on [account.ungleich.ch](https://account.ungleich.ch/) and
allows customers manage their ungleich account (register, edit mail & password,
...).
* login: dump-via-wget/datacenterlight.ch/en-us/hosting/login.html
Makes use of Django 2.2 LTS (not compatible with 3.0 at the moment) which is
supported until early 2022.
## Development Setup
* Clone this repository and enter top-level directory.
* (Optional) Setup a Python virtualenv and install dependencies via pip:
- `virtualenv .venv`
- `source .venv/bin/activate`
- `pip install -r requirements.txt`
- Note: you might have to install some OS dependencies (i.e. libldap2, libsasl).
* Configure the `dal` django app (uses the [decouple](https://pypi.org/project/python-decouple/) library underneath)
- Copy `dal/env.sample` to `dal/.env`
- Populate `dal/.env`
# Create .env to be loaded automatically
# XXX: Salvaged from production, don't ask me why some settings are duplicated.
LDAPSERVER="ldap://ldap1.ungleich.ch ldap://ldap2.ungleich.ch"
LDAPSEARCHUSER="user here"
LDAPSEARCHUSERPASSWORD="password here"
LDAPSERVER=""
LDAPSEARCHUSER=""
LDAPSEARCHUSERPASSWORD=""
LDAP_CUSTOMER_DN=""
LDAP_USERS_DN=""
LDAP_SERVER=""
LDAP_ADMIN_DN=""
LDAP_ADMIN_PASSWORD=""
# Space separated list of search bases for users
LDAPSEARCH="ou=users,dc=ungleich,dc=ch ou=customers,dc=ungleich,dc=ch"
LDAPCREATE="ou=customers,dc=ungleich,dc=ch"
LDAPSEARCH=""
LDAPCREATE=""
LDAP_CUSTOMER_DN=""
LDAP_DEFAULT_START_UID=
LDAP_CUSTOMER_GROUP_ID=
ENTIRE_SEARCH_BASE=""
#LDAP_USE_TLS=True
SECRET_KEY=""
ALLOWED_HOSTS="localhost:8000,localhost"
DEBUG=True
ENABLE_DEBUG_LOG=True
MODULES_TO_LOG=django_auth_ldap,dal,django
EMAIL_FROM_ADDRESS=""
#Otp
ALLOWED_REALMS=''
OTPSERVER=''
ADMIN_SEED=''
ADMIN_NAME=''
ADMIN_REALM=''
USER_REALM=''
......@@ -4,18 +4,18 @@ from django.utils.translation import ugettext_lazy as _
class LoginForm(forms.Form):
email = forms.CharField(widget=forms.TextInput())
username = forms.CharField(widget=forms.TextInput())
password = forms.CharField(widget=forms.PasswordInput())
class Meta:
fields = ['email', 'password']
fields = ['username', 'password']
def clean(self):
email = self.cleaned_data.get('email')
username = self.cleaned_data.get('username')
password = self.cleaned_data.get('password')
if self.errors:
return self.cleaned_data
is_auth = authenticate(username=email, password=password)
is_auth = authenticate(username=username, password=password)
if not is_auth:
raise forms.ValidationError(
_("Your username and/or password were incorrect.")
......@@ -26,6 +26,6 @@ class LoginForm(forms.Form):
# )
return self.cleaned_data
def clean_email(self):
email = self.cleaned_data.get('email')
return email
# XXX: is that thing used? Or useful?
def clean_username(self):
return self.cleaned_data.get('username')
......@@ -16,6 +16,7 @@ from .forms import LoginForm
from .ungleich_ldap import LdapManager
import logging
import re
logger = logging.getLogger(__name__)
......@@ -30,6 +31,9 @@ import string
from django.conf import settings
from django.contrib.auth.mixins import LoginRequiredMixin
# Small helper used for registration.
def is_username_valid(username):
return re.fullmatch(r"^[a-z|0-9|\-|_]+$", username)
class Index(FormView):
template_name = "landing.html"
......@@ -37,9 +41,9 @@ class Index(FormView):
success_url = 'useroptions.html'
def form_valid(self, form):
email = form.cleaned_data.get('email')
username = form.cleaned_data.get('username')
password = form.cleaned_data.get('password')
user = authenticate(username=email, password=password)
user = authenticate(username=username, password=password)
if user is not None:
login(self.request, user)
return render(self.request, 'useroptions.html', { 'user': user } )
......@@ -64,7 +68,16 @@ class Register(View):
username = request.POST.get('username')
if username == "" or not username:
return render(request, 'error.html', { 'urlname': urlname, 'service': service, 'error': 'Please supply a username.' } )
return render(request, 'error.html', {
'urlname': urlname,
'service': service,
'error': 'Please supply a username.' } )
if not is_username_valid(username):
return render(request, 'error.html', {
'urlname': urlname,
'service': service,
'error': 'You can only use lowercase letters, numbers, underscores and the dash character in your username.' } )
password1 = request.POST.get('password1')
password2 = request.POST.get('password2')
......@@ -491,7 +504,6 @@ class PseudoUser():
class UserCreateAPI(APIView):
def post(self, request):
username = request.POST.get('username')
email = request.POST.get('email')
firstname = request.POST.get('firstname')
......@@ -499,6 +511,10 @@ class UserCreateAPI(APIView):
if username == "" or not username:
return Response('Please supply a username.', 400)
if not is_username_valid(username):
return Response('Username is not valid.', 400)
try:
validate_email(email)
except ValidationError:
......
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<meta name="description" content="">
<meta name="author" content="">
<title>ungleich</title>
<!-- Bootstrap Core CSS -->
<link href="../../static/datacenterlight/css/bootstrap-3.3.7.min.css" rel="stylesheet">
<!-- Custom CSS -->
<link href="../../static/hosting/css/landing-page.css" rel="stylesheet">
<link href="../../static/hosting/css/user_keys.css" rel="stylesheet">
<link href="../../static/hosting/css/payment.css" rel="stylesheet">
<link href="../../static/hosting/css/order.css" rel="stylesheet">
<link href="../../static/hosting/css/orders.css" rel="stylesheet">
<link href="../../static/hosting/css/commons.css" rel="stylesheet">
<link href="../../static/hosting/css/virtual-machine.css" rel="stylesheet">
<link href="../../static/hosting/css/dashboard.css" rel="stylesheet">
<link href="../../static/hosting/css/price_calculator.css" rel="stylesheet">
<!-- Custom Fonts -->
<link href='https://fonts.googleapis.com/css?family=Raleway' rel='stylesheet' type='text/css'>
<link href="../../static/datacenterlight/font-awesome/css/font-awesome.min.css" rel="stylesheet" type="text/css">
<link href="https://fonts.googleapis.com/css?family=Lato:300,400,500,700,300italic,400italic,700italic" rel="stylesheet" type="text/css">
<link rel="shortcut icon" href="../../static/datacenterlight/img/favicon.ico" type="image/x-icon" />
<!-- HTML5 Shim and Respond.js IE8 support of HTML5 elements and media queries -->
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
<!--[if lt IE 9]>
<script src="https://oss.maxcdn.com/libs/html5shiv/3.7.0/html5shiv.js"></script>
<script src="https://oss.maxcdn.com/libs/respond.js/1.4.2/respond.min.js"></script>
<![endif]-->
<!-- Google analytics -->
<script>
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
ga('create', 'UA-62285904-8', 'auto');
ga('send', 'pageview');
</script>
<!-- End Google Analytics -->
</head>
<body>
<nav class="navbar navbar-default topnav navbar-transparent" role="navigation">
<div class="container">
<div class="topnav">
<!-- Brand and toggle get grouped for better mobile display -->
<div class="navbar-header">
<a id="logoWhite" class="navbar-brand topnav" href="https://datacenterlight.ch/en-us/datacenterlight/"><img src="../../static/datacenterlight/img/logo_white.svg"></a>
</div>
</div>
<!-- /.container -->
</div>
</nav>
<div class="">
<div class="auth-container">
<div class="auth-bg"></div>
<div class="auth-center">
<div class="auth-content">
<div class="auth-box">
<h1 class="section-heading allcaps">Log in</h1>
<form action="login" method="post" class="form" novalidated>
<input type='hidden' name='csrfmiddlewaretoken' value='frArvAmSKYWES7I5HmZc1fW6cTOdmJnS' />
<div class="form-group"><label class="sr-only control-label" for="id_email">Email</label><input class="form-control" id="id_email" name="email" placeholder="Email" required="required" title="" type="email" /></div>
<div class="form-group"><label class="sr-only control-label" for="id_password">Password</label><input class="form-control" id="id_password" name="password" placeholder="Password" required="required" title="" type="password" /></div>
<p class="red"></p>
<div class="text-center">
<button type="submit" class="btn choice-btn">
Log in
</button>
</div>
<input type='hidden' name='next' value=''/>
</form>
<div class="auth-footer">
<div>
Don't have an account yet ?&nbsp;
<a href="https://datacenterlight.ch/en-us/hosting/signup">Sign up</a>
</div>
<div>
or <a href="https://datacenterlight.ch/en-us/hosting/reset-password">Forgot your password ?</a><br>
or <a href="https://datacenterlight.ch/en-us/hosting/resend-activation-link">Resend activation link</a>
</div>
</div>
</div>
</div>
</div>
</div>
</div>
<!-- Footer -->
<div class="footer-light">
<footer>
<div class="container">
<ul class="list-inline">
<li>
<a class="url-init" href="https://datacenterlight.ch">Home</a>
</li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a class="url-init" href="https://datacenterlight.ch#contact">Contact</a>
</li>
<li class="footer-menu-divider">&sdot;</li>
<li>
<a class="url-init" href="https://datacenterlight.ch/cms/terms-of-service">Terms of Service</a>
</li>
</ul>
<p class="copyright text-muted small">Copyright &copy; ungleich glarus ag 2018. All Rights Reserved</p>
</div>
</footer>
</div>
<!-- jQuery -->
<script src="../../static/datacenterlight/js/jquery-2.2.4.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery-validate/1.16.0/jquery.validate.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/1000hz-bootstrap-validator/0.11.9/validator.min.js"></script>
<!-- Copy Clipboard -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/1.5.10/clipboard.min.js"></script>
<!-- Bootstrap Core JavaScript -->
<script src="../../static/datacenterlight/js/bootstrap-3.3.7.min.js"></script>
<!-- Virtual Machine Detail Javascript -->
<script src="../../static/hosting/js/virtual_machine_detail.js"></script>
<!-- Create Virtual Machine Javascript -->
<script src="../../static/hosting/js/createvm.js"></script>
<!-- Init JavaScript -->
<script src="../../static/hosting/js/initial.js"></script>
<script src="https://js.stripe.com/v3/"></script>
<script src="https://js.stripe.com/v2/"></script>
<!-- Stripe Lib -->
<!-- Proccess payment lib -->
<script type="text/javascript" src="../../static/hosting/js/payment.js"></script>
<!-- Gen SSH Key lib -->
<script type="text/javascript" src="../../static/hosting/js/gen-ssh-key.js"></script>
<!-- Moment -->
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<script type="text/javascript" src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment-with-locales.js"></script>
</body>
</html>
This source diff could not be displayed because it is too large. You can view the blob instead.
This source diff could not be displayed because it is too large. You can view the blob instead.
<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 144.75 20.83"><defs><style>.cls-1{fill:#fff;}</style></defs><title>logo_white</title><path class="cls-1" d="M12.93,23V21.55h0a3.49,3.49,0,0,1-1.54,1.31,5,5,0,0,1-2.12.46A5,5,0,0,1,7,22.83,5.17,5.17,0,0,1,5.3,21.52a5.77,5.77,0,0,1-1-1.9,7,7,0,0,1-.36-2.24,6.76,6.76,0,0,1,.37-2.23,5.83,5.83,0,0,1,1.05-1.87,4.93,4.93,0,0,1,3.85-1.75,4.26,4.26,0,0,1,2.09.47,4.64,4.64,0,0,1,1.32,1h0V6h3.68V23Zm-.14-5.61a3,3,0,0,0-.19-1.06,3,3,0,0,0-.54-.92,2.68,2.68,0,0,0-.84-.65,2.49,2.49,0,0,0-1.12-.25A2.54,2.54,0,0,0,9,14.77a2.58,2.58,0,0,0-1.34,1.55,3.39,3.39,0,0,0-.17,1.06,3.5,3.5,0,0,0,.17,1.07,2.75,2.75,0,0,0,.51.93,2.48,2.48,0,0,0,2,.9A2.48,2.48,0,0,0,11.22,20a2.67,2.67,0,0,0,.84-.65,3,3,0,0,0,.54-.92A3,3,0,0,0,12.79,17.4Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M19.08,13.35a6.47,6.47,0,0,1,2.3-1.41,7.88,7.88,0,0,1,2.68-.47,7.17,7.17,0,0,1,2.39.35A3.56,3.56,0,0,1,28,12.89a4.6,4.6,0,0,1,.89,1.84,10.88,10.88,0,0,1,.28,2.64V23H25.84V21.82h-.07a2.78,2.78,0,0,1-1.29,1.08,4.6,4.6,0,0,1-1.87.38,5.78,5.78,0,0,1-1.39-.18,4.06,4.06,0,0,1-1.31-.58,3.16,3.16,0,0,1-1-1.08,3.32,3.32,0,0,1-.38-1.66,2.82,2.82,0,0,1,.66-2,4.36,4.36,0,0,1,1.71-1.15,9,9,0,0,1,2.32-.54A23.77,23.77,0,0,1,25.73,16v-.18a1.37,1.37,0,0,0-.58-1.23,2.51,2.51,0,0,0-1.44-.39,3.57,3.57,0,0,0-1.52.34,4.84,4.84,0,0,0-1.25.81Zm6.76,4.78h-.47c-.4,0-.81,0-1.22.06a5,5,0,0,0-1.1.21,2,2,0,0,0-.8.46,1.06,1.06,0,0,0-.31.8,1,1,0,0,0,.15.54,1.12,1.12,0,0,0,.37.36,1.57,1.57,0,0,0,.52.19,3,3,0,0,0,.56.06,2.27,2.27,0,0,0,1.72-.62,2.31,2.31,0,0,0,.59-1.67v-.38Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M36.07,14.57v4.28a1.93,1.93,0,0,0,.3,1.18,1.3,1.3,0,0,0,1.09.4,3.91,3.91,0,0,0,.57,0,1.88,1.88,0,0,0,.51-.14l0,2.69a6,6,0,0,1-1,.24,6.88,6.88,0,0,1-1.17.1A5.51,5.51,0,0,1,34.56,23a2.93,2.93,0,0,1-1.22-.81,3.18,3.18,0,0,1-.66-1.26,6.12,6.12,0,0,1-.2-1.63V14.57h-1.8V11.83h1.77V8.89h3.62v2.94h2.63v2.74H36.07Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M41,13.35a6.47,6.47,0,0,1,2.3-1.41,7.88,7.88,0,0,1,2.68-.47,7.18,7.18,0,0,1,2.39.35,3.56,3.56,0,0,1,1.58,1.08,4.6,4.6,0,0,1,.89,1.84,10.83,10.83,0,0,1,.28,2.64V23H47.72V21.82h-.07a2.78,2.78,0,0,1-1.29,1.08,4.6,4.6,0,0,1-1.87.38,5.77,5.77,0,0,1-1.39-.18,4.08,4.08,0,0,1-1.31-.58,3.16,3.16,0,0,1-1-1.08,3.33,3.33,0,0,1-.38-1.66,2.82,2.82,0,0,1,.66-2,4.36,4.36,0,0,1,1.71-1.15,9,9,0,0,1,2.32-.54A23.77,23.77,0,0,1,47.61,16v-.18A1.37,1.37,0,0,0,47,14.6a2.51,2.51,0,0,0-1.44-.39,3.57,3.57,0,0,0-1.52.34,4.86,4.86,0,0,0-1.25.81Zm6.76,4.78h-.47c-.4,0-.81,0-1.22.06a5,5,0,0,0-1.1.21,2,2,0,0,0-.8.46,1.06,1.06,0,0,0-.31.8,1,1,0,0,0,.15.54,1.11,1.11,0,0,0,.37.36,1.57,1.57,0,0,0,.52.19,3,3,0,0,0,.56.06,2.27,2.27,0,0,0,1.72-.62,2.31,2.31,0,0,0,.6-1.67v-.38Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M61.87,15.2a4.27,4.27,0,0,0-1.18-.89,3.31,3.31,0,0,0-1.47-.3,3.26,3.26,0,0,0-1.45.3,3,3,0,0,0-1,.83,3.74,3.74,0,0,0-.63,1.21,4.78,4.78,0,0,0-.21,1.43,4,4,0,0,0,.25,1.4,3.53,3.53,0,0,0,.7,1.16,3.2,3.2,0,0,0,1.08.77,3.42,3.42,0,0,0,1.42.28,3.23,3.23,0,0,0,1.46-.3,3.74,3.74,0,0,0,1.12-.89l1.44,1.44a4.52,4.52,0,0,1-1.83,1.26,6.4,6.4,0,0,1-2.21.38,6.1,6.1,0,0,1-2.26-.4,5.19,5.19,0,0,1-1.76-1.14A5,5,0,0,1,54.15,20a6.08,6.08,0,0,1-.4-2.26,6.19,6.19,0,0,1,.4-2.27A5.06,5.06,0,0,1,57,12.54a6,6,0,0,1,2.28-.42,6.11,6.11,0,0,1,2.24.42A4.92,4.92,0,0,1,63.4,13.8Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M66.61,18.43a2.66,2.66,0,0,0,.3,1.27,3.14,3.14,0,0,0,.8,1,3.68,3.68,0,0,0,1.14.63,4.11,4.11,0,0,0,1.35.23,3,3,0,0,0,1.64-.44,5.28,5.28,0,0,0,1.28-1.16l1.53,1.17a5.62,5.62,0,0,1-4.72,2.18,5.87,5.87,0,0,1-2.28-.43,5,5,0,0,1-1.73-1.18,5.21,5.21,0,0,1-1.09-1.77,6.28,6.28,0,0,1-.38-2.21,5.82,5.82,0,0,1,.42-2.21A5.45,5.45,0,0,1,66,13.71a5.24,5.24,0,0,1,1.74-1.18A5.6,5.6,0,0,1,70,12.1a5.26,5.26,0,0,1,2.39.49A4.71,4.71,0,0,1,74,13.89a5.08,5.08,0,0,1,.91,1.8,7.46,7.46,0,0,1,.28,2v.72H66.61ZM73,16.81a4.3,4.3,0,0,0-.21-1.23,2.74,2.74,0,0,0-.57-1,2.65,2.65,0,0,0-1-.65,3.5,3.5,0,0,0-1.34-.24,3.25,3.25,0,0,0-1.36.28,3.3,3.3,0,0,0-1,.73,3.4,3.4,0,0,0-.66,1,2.77,2.77,0,0,0-.24,1.09Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M77.57,12.37h2V14h0A3,3,0,0,1,81,12.63a4.38,4.38,0,0,1,2.18-.53,4.63,4.63,0,0,1,1.47.24,3.31,3.31,0,0,1,1.24.73,3.5,3.5,0,0,1,.84,1.27A4.86,4.86,0,0,1,87,16.16V23H85V16.73a3.55,3.55,0,0,0-.2-1.27,2.31,2.31,0,0,0-.54-.85,2,2,0,0,0-.78-.47,2.84,2.84,0,0,0-.91-.15,3.29,3.29,0,0,0-1.17.2,2.44,2.44,0,0,0-.94.64,3,3,0,0,0-.63,1.11,5,5,0,0,0-.22,1.6V23h-2V12.37Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M95.76,14.12h-2.9v4.83c0,.3,0,.59,0,.89a2.29,2.29,0,0,0,.17.79,1.26,1.26,0,0,0,.45.56,1.52,1.52,0,0,0,.89.21,4.26,4.26,0,0,0,.74-.07,2.14,2.14,0,0,0,.7-.25v1.84a2.61,2.61,0,0,1-.93.28,6.92,6.92,0,0,1-.89.08A4.08,4.08,0,0,1,92.2,23a2.22,2.22,0,0,1-.94-.84A2.72,2.72,0,0,1,90.9,21c0-.43-.06-.86-.06-1.29V14.12H88.51V12.37h2.34v-3h2v3h2.9v1.75Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M99.89,18.43a2.67,2.67,0,0,0,.3,1.27,3.15,3.15,0,0,0,.8,1,3.67,3.67,0,0,0,1.15.63,4.1,4.1,0,0,0,1.35.23,3,3,0,0,0,1.64-.44,5.28,5.28,0,0,0,1.28-1.16l1.53,1.17a5.62,5.62,0,0,1-4.72,2.18,5.87,5.87,0,0,1-2.28-.43,5,5,0,0,1-1.73-1.18,5.23,5.23,0,0,1-1.09-1.77,6.24,6.24,0,0,1-.38-2.21,5.82,5.82,0,0,1,.42-2.21,5.46,5.46,0,0,1,1.14-1.77A5.25,5.25,0,0,1,101,12.53a5.61,5.61,0,0,1,2.2-.43,5.27,5.27,0,0,1,2.39.49,4.74,4.74,0,0,1,1.61,1.29,5.07,5.07,0,0,1,.91,1.8,7.46,7.46,0,0,1,.28,2v.72H99.89Zm6.38-1.62a4.3,4.3,0,0,0-.21-1.23,2.74,2.74,0,0,0-.57-1,2.65,2.65,0,0,0-1-.65,3.5,3.5,0,0,0-1.34-.24,3.25,3.25,0,0,0-1.36.28,3.33,3.33,0,0,0-1,.73,3.37,3.37,0,0,0-.66,1,2.75,2.75,0,0,0-.24,1.09Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M110.85,12.37h2V14h0a2.88,2.88,0,0,1,.54-.77,3.6,3.6,0,0,1,.75-.6,4,4,0,0,1,.91-.39,3.49,3.49,0,0,1,1-.15,2.83,2.83,0,0,1,.9.14l-.09,2.18-.49-.11a2.84,2.84,0,0,0-.49,0,3,3,0,0,0-2.27.83,3.65,3.65,0,0,0-.79,2.58V23h-2V12.37Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M120.16,5.51h1.37V17h-1.37Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M123.49,7.22a1,1,0,1,1,.3.71A1,1,0,0,1,123.49,7.22Zm.32,2.58h1.37V17h-1.37Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M134.57,16.94a3.87,3.87,0,0,1-.28,1.5,3.42,3.42,0,0,1-.8,1.17,3.62,3.62,0,0,1-1.25.77,4.55,4.55,0,0,1-1.61.27,5.94,5.94,0,0,1-1.89-.29,4.58,4.58,0,0,1-1.63-1l.93-1.16a3.76,3.76,0,0,0,1.16.87,3.25,3.25,0,0,0,1.41.3,3.14,3.14,0,0,0,1.26-.22,2.17,2.17,0,0,0,.8-.57,2.06,2.06,0,0,0,.42-.8,3.45,3.45,0,0,0,.12-.9V15.82h0a2.41,2.41,0,0,1-1.07,1,3.36,3.36,0,0,1-1.42.31,3.8,3.8,0,0,1-1.47-.28A3.5,3.5,0,0,1,128,16a3.55,3.55,0,0,1-.76-1.17A3.86,3.86,0,0,1,127,13.4a4.18,4.18,0,0,1,.26-1.48,3.61,3.61,0,0,1,.74-1.21,3.34,3.34,0,0,1,1.15-.81,3.82,3.82,0,0,1,1.51-.29,3.35,3.35,0,0,1,1.42.32,2.62,2.62,0,0,1,1.09.9h0v-1h1.37v7.15Zm-3.74-6.05a2.47,2.47,0,0,0-1,.19,2.17,2.17,0,0,0-.74.52,2.29,2.29,0,0,0-.47.79,3,3,0,0,0-.17,1,2.43,2.43,0,0,0,.64,1.76,2.6,2.6,0,0,0,3.47,0,2.42,2.42,0,0,0,.64-1.76,3,3,0,0,0-.17-1,2.3,2.3,0,0,0-.47-.79,2.19,2.19,0,0,0-.74-.52A2.48,2.48,0,0,0,130.83,10.89Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M136.29,5.51h1.37v5.4h0a2,2,0,0,1,.9-.94,3,3,0,0,1,1.48-.36,3.11,3.11,0,0,1,1,.16,2.25,2.25,0,0,1,.84.49,2.37,2.37,0,0,1,.57.86,3.3,3.3,0,0,1,.21,1.24V17h-1.37V12.75a2.41,2.41,0,0,0-.14-.86,1.58,1.58,0,0,0-.37-.58,1.39,1.39,0,0,0-.52-.32,2,2,0,0,0-.62-.1,2.24,2.24,0,0,0-.79.14,1.65,1.65,0,0,0-.64.43,2,2,0,0,0-.43.75,3.44,3.44,0,0,0-.15,1.08V17h-1.37V5.51Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M148.6,11h-2v3.27c0,.2,0,.4,0,.6a1.57,1.57,0,0,0,.11.53.86.86,0,0,0,.3.38,1,1,0,0,0,.6.15,2.94,2.94,0,0,0,.5,0,1.44,1.44,0,0,0,.47-.17v1.25a1.78,1.78,0,0,1-.63.19,4.8,4.8,0,0,1-.6.05,2.73,2.73,0,0,1-1.22-.22,1.5,1.5,0,0,1-.64-.57,1.84,1.84,0,0,1-.24-.78c0-.29,0-.58,0-.87V11h-1.58V9.8h1.58v-2h1.37v2h2V11Z" transform="translate(-3.9 -5.51)"/><path class="cls-1" d="M142.23,20.31a2.45,2.45,0,0,1-.13.43L142,21l-.17.38c-.08.18,0,0-.14.27a9.2,9.2,0,0,1-8.38,4.66c-3.92-.09-6.72-1.81-8.07-4.17-.1-.18-.26-.42-.38-.68,0-.07-.24-.51-.26-.58-.12-.33-.2-.37-.22-.59a5.13,5.13,0,0,0,.78,1.09,11.19,11.19,0,0,0,8.16,3.48,11,11,0,0,0,8.16-3.48A4.85,4.85,0,0,0,142.23,20.31Z" transform="translate(-3.9 -5.51)"/></svg>
\ No newline at end of file
@media (min-width: 768px) {
.navbar-right {
margin-right: 10px;
}
}
.dashboard-container {
padding-top: 80px;
padding-bottom: 70px;
width: 90%;
margin: 0 auto;
max-width: 768px;
}
.dashboard-container.wide {
padding-top: 90px;
max-width: 980px;
}
.content-dashboard{
min-height: calc(100vh - 60px);
width: 80%;
margin: 0 auto;
max-width: 1120px;
}
.container-table{
margin-top: 35px;
overflow-y: hidden;
}
.container-table table{
overflow-y: auto;
}
.borderless td {
border: none !important;
}
.borderless thead {
}
.borderless tbody:before {
content: "-";
display: block;
color: transparent;
}
.inline-headers h3, .inline-headers h4 {
display: inline-block;
vertical-align: baseline;
}
.space-above {
margin-top: 4%;
}
.space-above-big {
margin-top: 20%;
}
.table>tbody>tr>td{
vertical-align: middle;
}
.fa-separate{
margin-right: 15px;
}
@media (max-width: 540px) {
select {
width: 280px;
}
.content-dashboard {
padding-left: 15px;
padding-right: 15px;
width: 100%;
}
}
.btn:focus, .btn:active:focus {
outline: 0;
}
/***********Styles for Model********************/
.modal-content {
border-radius: 0px;
font-family: Lato, "Helvetica Neue", Helvetica, Arial, sans-serif;
width: 100%;
float: left;
border-radius: 0;
font-weight: 300;
}
.modal-header {
min-height: 30px;
}
.modal-header .close {
font-size: 75px;
font-weight: 300;
margin-top: 0;
position: absolute;
top: 0;
right: 11px;
z-index: 10;
line-height: 60px;
}
.modal-header .close span {
display: block;
}
.modal-header .close:focus {
outline: 0;
}
.modal-header {
border-bottom: 0px solid #e5e5e5;
padding: 0px 15px;
width: 100%;
}
.modal-body {
text-align: center;
width: 100%;
float: left;
padding: 0px 30px 15px 30px;
}
.modal-body .modal-icon i {
font-size: 80px;
font-weight: 100;
color: #999;
}
.modal-body .modal-icon {
margin-bottom: 15px;
}
.modal-title {
margin: 0;
line-height: 1.42857143;
font-size: 25px;
padding: 0;
/*font-family: 'Lato', sans-serif;*/
font-weight: 300;
}
.modal-text {
padding-top: 5px;
font-size: 16px;
}
.modal-text p:not(:last-of-type){
margin-bottom: 5px;
}
.modal-title + .modal-footer {
margin-top: 5px;
}
.modal-footer {
border-top: 0px solid #e5e5e5;
width: 100%;
float: left;
text-align: center;
padding: 15px 15px;
}
@media (min-width: 1300px) {
.modal-dialog {/* top: 30%; */width: 40%;}
}
@media (max-width: 1299px) {
.modal-dialog {
/* top: 20%; */
width: 43%;
}
}
@media (max-width: 900px) {
.modal-dialog {
/* top: 20%; */
width: 50%;
}
}
@media (max-width: 767px) {
.modal-dialog {
/* top: 30%; */
width: 95%;
margin: 0 auto !important;
}
}
/* ========= */
@media(min-width: 320px) {
.modal:before {
content: '';
display: inline-block;
height: 100%;
vertical-align: middle;
margin-right: -4px;
}
}
@media (min-width: 768px) {
.modal-dialog {
/* width: 520px; */
margin: 15px auto;
}
}
.modal {
text-align: center;
}
.modal-dialog {
display: inline-block;
text-align: left;
vertical-align: middle;
}
.un-icon {
width: 15px;
height: 15px;
opacity: 0.5;
margin-top: -1px;
}
.css-plus {
position: relative;
width: 16px;
height: 20px;
display: inline-block;