mirror of
https://github.com/jwinterm/modoboa.git
synced 2026-10-17 05:41:25 +02:00
Merge branch 'master' into feature/score-and-sorting-in-quarantine
This commit is contained in:
@@ -118,9 +118,9 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
)
|
||||
|
||||
ldap_search_base = forms.CharField(
|
||||
label=ugettext_lazy("Search base"),
|
||||
label=ugettext_lazy("Users search base"),
|
||||
initial="",
|
||||
help_text=ugettext_lazy("The distinguished name of the search base"),
|
||||
help_text=ugettext_lazy("The distinguished name of the search base used to find users"),
|
||||
required=False
|
||||
)
|
||||
|
||||
@@ -150,6 +150,25 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
help_text=ugettext_lazy("Tell if the LDAP server is an Active Directory one")
|
||||
)
|
||||
|
||||
ldap_admin_groups = forms.CharField(
|
||||
label=ugettext_lazy("Administrator groups"),
|
||||
initial="",
|
||||
help_text=ugettext_lazy(
|
||||
"Members of those LDAP Posix groups will be created as domain "
|
||||
"administrators. Use ';' characters to separate groups."
|
||||
),
|
||||
required=False
|
||||
)
|
||||
|
||||
ldap_groups_search_base = forms.CharField(
|
||||
label=ugettext_lazy("Groups search base"),
|
||||
initial="",
|
||||
help_text=ugettext_lazy(
|
||||
"The distinguished name of the search base used to find groups"
|
||||
),
|
||||
required=False
|
||||
)
|
||||
|
||||
sep3 = SeparatorField(label=ugettext_lazy("Miscellaneous"))
|
||||
|
||||
log_maximum_age = forms.IntegerField(
|
||||
@@ -185,6 +204,8 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
"ldap_user_dn_template": "ldap_auth_method=directbind",
|
||||
"ldap_password_attribute": "authentication_type=ldap",
|
||||
"ldap_is_active_directory": "authentication_type=ldap",
|
||||
"ldap_admin_groups": "authentication_type=ldap",
|
||||
"ldap_groups_search_base": "authentication_type=ldap",
|
||||
}
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
@@ -243,7 +264,7 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
"""
|
||||
try:
|
||||
import ldap
|
||||
from django_auth_ldap.config import LDAPSearch
|
||||
from django_auth_ldap.config import LDAPSearch, PosixGroupType
|
||||
ldap_available = True
|
||||
except ImportError:
|
||||
ldap_available = False
|
||||
@@ -260,6 +281,11 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
ldap_uri = 'ldaps://' if values["ldap_secured"] == "yes" else "ldap://"
|
||||
ldap_uri += "%s:%s" % (values["ldap_server_address"], values["ldap_server_port"])
|
||||
setattr(settings, "AUTH_LDAP_SERVER_URI", ldap_uri)
|
||||
setattr(settings, "AUTH_LDAP_GROUP_TYPE", PosixGroupType())
|
||||
setattr(settings, "AUTH_LDAP_GROUP_SEARCH", LDAPSearch(
|
||||
values["ldap_groups_search_base"], ldap.SCOPE_SUBTREE,
|
||||
"(objectClass=posixGroup)"
|
||||
))
|
||||
if values["ldap_auth_method"] == "searchbind":
|
||||
setattr(settings, "AUTH_LDAP_BIND_DN", values["ldap_bind_dn"])
|
||||
setattr(settings, "AUTH_LDAP_BIND_PASSWORD", values["ldap_bind_password"])
|
||||
@@ -270,7 +296,6 @@ class GeneralParametersForm(parameters.AdminParametersForm):
|
||||
setattr(settings, "AUTH_LDAP_USER_SEARCH", search)
|
||||
else:
|
||||
setattr(settings, "AUTH_LDAP_USER_DN_TEMPLATE", values["ldap_user_dn_template"])
|
||||
|
||||
if values["ldap_is_active_directory"] == "yes":
|
||||
if not hasattr(settings, "AUTH_LDAP_GLOBAL_OPTIONS"):
|
||||
setattr(settings, "AUTH_LDAP_GLOBAL_OPTIONS", {
|
||||
|
||||
@@ -108,7 +108,7 @@ class User(PermissionsMixin):
|
||||
:param raw_value: the new password's value
|
||||
:param curvalue: the current password (for LDAP authentication)
|
||||
"""
|
||||
if parameters.get_admin("AUTHENTICATION_TYPE") == "local":
|
||||
if self.is_local:
|
||||
self.password = self._crypt_password(raw_value)
|
||||
else:
|
||||
if not ldap_available:
|
||||
@@ -363,7 +363,7 @@ class User(PermissionsMixin):
|
||||
reversion.register(User)
|
||||
|
||||
|
||||
def populate_callback(user):
|
||||
def populate_callback(user, group='SimpleUsers'):
|
||||
"""Populate callback
|
||||
|
||||
If the LDAP authentication backend is in use, this callback will
|
||||
@@ -376,7 +376,7 @@ def populate_callback(user):
|
||||
from modoboa.lib.permissions import grant_access_to_object
|
||||
|
||||
sadmins = User.objects.filter(is_superuser=True)
|
||||
user.set_role("SimpleUsers")
|
||||
user.set_role(group)
|
||||
user.post_create(sadmins[0])
|
||||
for su in sadmins[1:]:
|
||||
grant_access_to_object(su, user)
|
||||
|
||||
@@ -16,7 +16,10 @@ class MD5CRYPTHasher(PasswordHasher):
|
||||
"""
|
||||
@property
|
||||
def scheme(self):
|
||||
return '{MD5-CRYPT}'
|
||||
return '{MD5-CRYPT}' if self._target == 'local' else '{CRYPT}'
|
||||
|
||||
def _b64encode(self, pwhash):
|
||||
return pwhash
|
||||
|
||||
def _encrypt(self, clearvalue, salt=None):
|
||||
return md5_crypt.encrypt(clearvalue)
|
||||
@@ -34,7 +37,10 @@ class SHA256CRYPTHasher(PasswordHasher):
|
||||
"""
|
||||
@property
|
||||
def scheme(self):
|
||||
return '{SHA256-CRYPT}'
|
||||
return '{SHA256-CRYPT}' if self._target == 'local' else '{CRYPT}'
|
||||
|
||||
def _b64encode(self, pwhash):
|
||||
return pwhash
|
||||
|
||||
def _encrypt(self, clearvalue, salt=None):
|
||||
rounds = int(parameters.get_admin('ROUNDS_NUMBER'))
|
||||
@@ -53,7 +59,10 @@ class SHA512CRYPTHasher(PasswordHasher):
|
||||
"""
|
||||
@property
|
||||
def scheme(self):
|
||||
return '{SHA512-CRYPT}'
|
||||
return '{SHA512-CRYPT}' if self._target == 'local' else '{CRYPT}'
|
||||
|
||||
def _b64encode(self, pwhash):
|
||||
return pwhash
|
||||
|
||||
def _encrypt(self, clearvalue, salt=None):
|
||||
rounds = int(parameters.get_admin('ROUNDS_NUMBER'))
|
||||
|
||||
@@ -15,6 +15,22 @@ class PasswordHasher(object):
|
||||
"""
|
||||
Base class of all hashers.
|
||||
"""
|
||||
def __init__(self, target='local'):
|
||||
self._target = target
|
||||
|
||||
def _encrypt(self, clearvalue, salt=None):
|
||||
raise NotImplementedError
|
||||
|
||||
def _b64encode(self, pwhash):
|
||||
"""Encode :keyword:`pwhash` using base64 if needed.
|
||||
|
||||
:param str pwhash: password hash
|
||||
:return: base64 encoded hash or original hash
|
||||
"""
|
||||
if self._target == 'ldap':
|
||||
return base64.b64encode(pwhash)
|
||||
return pwhash
|
||||
|
||||
def encrypt(self, clearvalue):
|
||||
"""Encrypt a password.
|
||||
|
||||
@@ -28,7 +44,8 @@ class PasswordHasher(object):
|
||||
:rtype: str
|
||||
:return: encrypted password
|
||||
"""
|
||||
return '%s%s' % (self.scheme, self._encrypt(clearvalue))
|
||||
pwhash = self._b64encode(self._encrypt(clearvalue))
|
||||
return '%s%s' % (self.scheme, pwhash)
|
||||
|
||||
def verify(self, clearvalue, hashed_value):
|
||||
"""Verify a password against a hashed value.
|
||||
@@ -38,7 +55,7 @@ class PasswordHasher(object):
|
||||
:return: True if passwords match, False otherwise
|
||||
"""
|
||||
return constant_time_compare(
|
||||
self._encrypt(clearvalue, hashed_value),
|
||||
self._b64encode(self._encrypt(clearvalue, hashed_value)),
|
||||
hashed_value
|
||||
)
|
||||
|
||||
@@ -97,4 +114,12 @@ class SHA256Hasher(PasswordHasher):
|
||||
return '{SHA256}'
|
||||
|
||||
def _encrypt(self, clearvalue, salt=None):
|
||||
return base64.b64encode(hashlib.sha256(clearvalue).digest())
|
||||
return hashlib.sha256(clearvalue).digest()
|
||||
|
||||
def _b64encode(self, pwhash):
|
||||
"""Encode :keyword:`pwhash` using base64.
|
||||
|
||||
:param str pwhash: password hash
|
||||
:return: base64 encoded hash
|
||||
"""
|
||||
return base64.b64encode(pwhash)
|
||||
|
||||
@@ -184,8 +184,10 @@ def account_auto_created(user):
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib.permissions import grant_access_to_object
|
||||
|
||||
sadmins = User.objects.filter(is_superuser=True)
|
||||
localpart, domname = split_mailbox(user.username)
|
||||
if user.group != 'SimpleUsers' and domname is None:
|
||||
return
|
||||
sadmins = User.objects.filter(is_superuser=True)
|
||||
try:
|
||||
domain = Domain.objects.get(name=domname)
|
||||
except Domain.DoesNotExist:
|
||||
|
||||
@@ -22,10 +22,20 @@ class AdminParametersForm(parameters.AdminParametersForm):
|
||||
help_text=ugettext_lazy("The UNIX account who owns mailboxes on the filesystem")
|
||||
)
|
||||
|
||||
default_domain_quota = forms.IntegerField(
|
||||
label=ugettext_lazy("Default domain quota"),
|
||||
initial=0,
|
||||
help_text=ugettext_lazy(
|
||||
"Default quota (in MB) applied to freshly created domains with no "
|
||||
"value specified. A value of 0 means no quota."
|
||||
),
|
||||
widget=forms.TextInput(attrs={'class': 'span2'})
|
||||
)
|
||||
|
||||
auto_account_removal = YesNoField(
|
||||
label=ugettext_lazy("Automatic account removal"),
|
||||
initial="no",
|
||||
help_text=ugettext_lazy("When a mailbox is removed, also remove the associated account"),
|
||||
help_text=ugettext_lazy("When a mailbox is removed, also remove the associated account")
|
||||
)
|
||||
|
||||
# Visibility rules
|
||||
@@ -46,3 +56,11 @@ class AdminParametersForm(parameters.AdminParametersForm):
|
||||
if hide_fields:
|
||||
del self.fields["handle_mailboxes"]
|
||||
del self.fields["mailboxes_owner"]
|
||||
|
||||
def clean_default_domain_quota(self):
|
||||
"""Ensure quota is a positive integer."""
|
||||
if self.cleaned_data['default_domain_quota'] < 0:
|
||||
raise forms.ValidationError(
|
||||
ugettext_lazy('Must be a positive integer')
|
||||
)
|
||||
return self.cleaned_data['default_domain_quota']
|
||||
|
||||
@@ -65,7 +65,7 @@ class AccountFormGeneral(forms.ModelForm):
|
||||
account = kwargs["instance"]
|
||||
self.fields["role"].initial = account.group
|
||||
if not account.is_local \
|
||||
and parameters.get_admin("LDAP_AUTH_METHOD") == "directbind":
|
||||
and parameters.get_admin("LDAP_AUTH_METHOD", app="core") == "directbind":
|
||||
del self.fields["password1"]
|
||||
del self.fields["password2"]
|
||||
|
||||
@@ -162,6 +162,22 @@ class AccountFormMail(forms.Form, DynamicForm):
|
||||
"""Ensure lower case emails"""
|
||||
return self.cleaned_data["email"].lower()
|
||||
|
||||
def clean(self):
|
||||
"""Custom fields validation.
|
||||
|
||||
Check if quota is >= 0 only when the domain value is not used.
|
||||
"""
|
||||
super(AccountFormMail, self).clean()
|
||||
if self._errors:
|
||||
raise forms.ValidationError(self._errors)
|
||||
if not self.cleaned_data["quota_act"] \
|
||||
and self.cleaned_data['quota'] is not None:
|
||||
if self.cleaned_data["quota"] < 0:
|
||||
self._errors["quota"] = self.error_class(
|
||||
[_("Must be a valid integer")])
|
||||
del self.cleaned_data["quota"]
|
||||
return self.cleaned_data
|
||||
|
||||
def create_mailbox(self, user, account):
|
||||
locpart, domname = split_mailbox(self.cleaned_data["email"])
|
||||
try:
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
from django import forms
|
||||
from django.http import QueryDict
|
||||
from django.utils.translation import ugettext as _, ugettext_lazy
|
||||
from modoboa.lib import events
|
||||
from modoboa.lib import events, parameters
|
||||
from modoboa.lib.exceptions import Conflict
|
||||
from modoboa.lib.formutils import (
|
||||
DomainNameField, YesNoField, DynamicForm, TabForms
|
||||
@@ -13,6 +13,15 @@ from modoboa.extensions.admin.models import (
|
||||
|
||||
|
||||
class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
quota = forms.IntegerField(
|
||||
label=ugettext_lazy("Quota"),
|
||||
required=False,
|
||||
help_text=ugettext_lazy(
|
||||
"Default quota in MB applied to mailboxes. Leave empty to use the "
|
||||
"default value."
|
||||
),
|
||||
widget=forms.TextInput(attrs={"class": "span1"})
|
||||
)
|
||||
aliases = DomainNameField(
|
||||
label=ugettext_lazy("Alias(es)"),
|
||||
required=False,
|
||||
@@ -25,9 +34,6 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
class Meta:
|
||||
model = Domain
|
||||
fields = ("name", "quota", "aliases", "enabled")
|
||||
widgets = dict(
|
||||
quota=forms.widgets.TextInput(attrs={"class": "span1"})
|
||||
)
|
||||
|
||||
def __init__(self, *args, **kwargs):
|
||||
self.oldname = None
|
||||
@@ -43,6 +49,13 @@ class DomainFormGeneral(forms.ModelForm, DynamicForm):
|
||||
name = "aliases_%d" % (pos + 1)
|
||||
self._create_field(forms.CharField, name, dalias.name, 3)
|
||||
|
||||
def clean_quota(self):
|
||||
if self.cleaned_data['quota'] is None:
|
||||
return int(parameters.get_admin('DEFAULT_DOMAIN_QUOTA'))
|
||||
if self.cleaned_data['quota'] < 0:
|
||||
raise forms.ValidationError(_("Must be a positive integer"))
|
||||
return self.cleaned_data['quota']
|
||||
|
||||
def clean(self):
|
||||
"""Custom fields validation.
|
||||
|
||||
|
||||
@@ -27,9 +27,7 @@ class Domain(AdminObject):
|
||||
"""
|
||||
name = models.CharField(ugettext_lazy('name'), max_length=100, unique=True,
|
||||
help_text=ugettext_lazy("The domain name"))
|
||||
quota = models.IntegerField(
|
||||
help_text=ugettext_lazy("Default quota in MB applied to mailboxes")
|
||||
)
|
||||
quota = models.IntegerField()
|
||||
enabled = models.BooleanField(
|
||||
ugettext_lazy('enabled'),
|
||||
help_text=ugettext_lazy("Check to activate this domain")
|
||||
|
||||
@@ -85,7 +85,8 @@ class PermissionsTestCase(ModoTestCase):
|
||||
self.user = User.objects.get(username='user@test.com')
|
||||
self.values = dict(
|
||||
username=self.user.username, role="DomainAdmins",
|
||||
is_active=self.user.is_active, email="user@test.com"
|
||||
is_active=self.user.is_active, email="user@test.com",
|
||||
quota_act=True
|
||||
)
|
||||
|
||||
def tearDown(self):
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
from django.core.urlresolvers import reverse
|
||||
from modoboa.core.models import User
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.tests import ModoTestCase
|
||||
from modoboa.extensions.admin.models import (
|
||||
Domain, Alias
|
||||
@@ -66,6 +67,22 @@ class DomainTestCase(ModoTestCase):
|
||||
self.assertIn(da.mailbox_set.all()[0], al.mboxes.all())
|
||||
self.assertTrue(da.can_access(al))
|
||||
|
||||
def test_create_using_default_quota(self):
|
||||
parameters.save_admin('DEFAULT_DOMAIN_QUOTA', 50, app='admin')
|
||||
values = {
|
||||
"name": "pouet.com", "create_dom_admin": "yes",
|
||||
"dom_admin_username": "toto", "create_aliases": "yes",
|
||||
"stepid": 'step2'
|
||||
}
|
||||
self.ajax_post(
|
||||
reverse("modoboa.extensions.admin.views.domain.newdomain"),
|
||||
values
|
||||
)
|
||||
dom = Domain.objects.get(name="pouet.com")
|
||||
self.assertEqual(dom.quota, 50)
|
||||
da = User.objects.get(username="toto@pouet.com")
|
||||
self.assertEqual(da.mailbox_set.all()[0].quota, 50)
|
||||
|
||||
def test_modify(self):
|
||||
"""Test the modification of a domain
|
||||
|
||||
|
||||
@@ -36,15 +36,23 @@ try:
|
||||
the user. ldap_user.dn is the user's DN and
|
||||
ldap_user.attrs contains all of their LDAP attributes.
|
||||
"""
|
||||
lpart, domain = split_mailbox(username)
|
||||
if domain is None:
|
||||
return None
|
||||
group = 'SimpleUsers'
|
||||
admin_groups = parameters \
|
||||
.get_admin('LDAP_ADMIN_GROUPS', app='core').split(';')
|
||||
for grp in admin_groups:
|
||||
if grp.strip() in ldap_user.group_names:
|
||||
group = 'DomainAdmins'
|
||||
break
|
||||
if group == 'SimpleUsers':
|
||||
lpart, domain = split_mailbox(username)
|
||||
if domain is None:
|
||||
return None
|
||||
user, created = User.objects.get_or_create(
|
||||
username__iexact=username,
|
||||
defaults={'username': username.lower(), 'is_local': False}
|
||||
)
|
||||
if created:
|
||||
populate_callback(user)
|
||||
populate_callback(user, group)
|
||||
return user, created
|
||||
|
||||
def get_user(self, user_id):
|
||||
|
||||
+10
-3
@@ -1,7 +1,10 @@
|
||||
# coding: utf-8
|
||||
from django.db import connection
|
||||
from django.conf import settings
|
||||
|
||||
from django.utils.translation import ugettext as _
|
||||
from modoboa.lib.exceptions import InternalError
|
||||
|
||||
|
||||
def db_table_exists(table, cursor=None):
|
||||
"""Check if table exists
|
||||
|
||||
@@ -20,14 +23,18 @@ def db_table_exists(table, cursor=None):
|
||||
else:
|
||||
return table in table_names
|
||||
|
||||
def db_type(connection="default"):
|
||||
|
||||
def db_type(cname="default"):
|
||||
"""Return the type of the *default* database
|
||||
|
||||
Supported values : 'postgres', 'mysql', 'sqlite'
|
||||
|
||||
:param str cname: connection name
|
||||
:return: a string or None
|
||||
"""
|
||||
if not cname in settings.DATABASES:
|
||||
raise InternalError(_("Connection to database %s not configured" % cname))
|
||||
for t in ['postgres', 'mysql', 'sqlite']:
|
||||
if settings.DATABASES[connection]['ENGINE'].find(t) != -1:
|
||||
if settings.DATABASES[cname]['ENGINE'].find(t) != -1:
|
||||
return t
|
||||
return None
|
||||
|
||||
@@ -147,10 +147,16 @@ class Email(object):
|
||||
return path
|
||||
|
||||
def map_cid(self, url):
|
||||
m = re.match(".*cid:(.+)", url)
|
||||
if m:
|
||||
if m.group(1) in self.attached_map:
|
||||
return self.attached_map[m.group(1)]
|
||||
"""Replace attachment links.
|
||||
|
||||
:param str url: original url
|
||||
:rtype: string
|
||||
:return: internal link
|
||||
"""
|
||||
match = re.match(".*cid:(.+)", url)
|
||||
if match is not None:
|
||||
if match.group(1) in self.attached_map:
|
||||
return self.attached_map[match.group(1)]
|
||||
return url
|
||||
|
||||
def render_headers(self, **kwargs):
|
||||
@@ -172,6 +178,8 @@ class Email(object):
|
||||
html.rewrite_links(lambda x: None)
|
||||
else:
|
||||
html.rewrite_links(self.map_cid)
|
||||
for link in html.iterlinks():
|
||||
link[0].set('target', '_blank')
|
||||
body = html.find("body")
|
||||
if body is None:
|
||||
body = lxml.html.tostring(html)
|
||||
|
||||
@@ -34,13 +34,9 @@ Extracted from `this blog
|
||||
|
||||
"""
|
||||
import ldap
|
||||
import base64
|
||||
import hashlib
|
||||
import string
|
||||
import crypt
|
||||
from random import Random
|
||||
from django.conf import settings
|
||||
from django.utils.translation import ugettext as _
|
||||
from modoboa.core.password_hashers import get_password_hasher
|
||||
from modoboa.lib import parameters
|
||||
from modoboa.lib.exceptions import InternalError
|
||||
|
||||
@@ -71,7 +67,7 @@ class LDAPAuthBackend(object):
|
||||
return conn
|
||||
|
||||
def connect_to_server(self, user, password):
|
||||
if parameters.get_admin("LDAP_AUTH_METHOD", app="admin") == "searchbind":
|
||||
if parameters.get_admin("LDAP_AUTH_METHOD", app="core") == "searchbind":
|
||||
bind_dn = self._setting("AUTH_LDAP_BIND_DN", "")
|
||||
bind_pwd = self._setting("AUTH_LDAP_BIND_PASSWORD", "")
|
||||
self.conn = self._get_conn(bind_dn, bind_pwd)
|
||||
@@ -82,8 +78,8 @@ class LDAPAuthBackend(object):
|
||||
self.conn = self._get_conn(self.user_dn, password)
|
||||
|
||||
def _find_user_dn(self, user):
|
||||
sbase = parameters.get_admin("LDAP_SEARCH_BASE", app="admin")
|
||||
sfilter = parameters.get_admin("LDAP_SEARCH_FILTER", app="admin")
|
||||
sbase = parameters.get_admin("LDAP_SEARCH_BASE", app="core")
|
||||
sfilter = parameters.get_admin("LDAP_SEARCH_FILTER", app="core")
|
||||
sfilter = sfilter % {"user": user}
|
||||
res = self.conn.search_s(sbase, ldap.SCOPE_SUBTREE, sfilter)
|
||||
try:
|
||||
@@ -95,25 +91,12 @@ class LDAPAuthBackend(object):
|
||||
def _crypt_password(self, clearpassword):
|
||||
"""Overidding of the crypt_password function (LDAP compliant)
|
||||
|
||||
The crypted password in base64 encoded and we prepend the used
|
||||
algorithm to the returned value. (between {})
|
||||
|
||||
:param clearpassword: the clear password
|
||||
:return: the encrypted password
|
||||
"""
|
||||
scheme = parameters.get_admin("PASSWORD_SCHEME", app="admin")
|
||||
if scheme == "crypt":
|
||||
salt = ''.join(Random().sample(string.letters + string.digits, 2))
|
||||
result = crypt.crypt(clearpassword, salt)
|
||||
elif scheme == "md5":
|
||||
obj = hashlib.md5(clearpassword)
|
||||
result = obj.digest()
|
||||
elif scheme == "sha256":
|
||||
obj = hashlib.sha256(clearpassword)
|
||||
result = obj.digest()
|
||||
else:
|
||||
return str(clearpassword)
|
||||
return str("{%s}%s" % (scheme.upper(), base64.b64encode(result)))
|
||||
scheme = parameters.get_admin("PASSWORD_SCHEME", app="core")
|
||||
hasher = get_password_hasher(scheme.upper())('ldap')
|
||||
return hasher.encrypt(clearpassword)
|
||||
|
||||
def update_user_password(self, user, password, newpassword):
|
||||
try:
|
||||
|
||||
@@ -8,7 +8,7 @@ import re
|
||||
import json
|
||||
from django.http import HttpResponse, HttpResponseRedirect
|
||||
from django.contrib.auth.decorators import login_required
|
||||
from django.shortcuts import render_to_response
|
||||
from django.shortcuts import render, render_to_response
|
||||
from django.template.loader import render_to_string
|
||||
from django import template
|
||||
from django.conf import settings
|
||||
@@ -27,9 +27,8 @@ def _render_to_string(request, tpl, user_context):
|
||||
def _render_error(request, errortpl="error", user_context=None):
|
||||
if user_context is None:
|
||||
user_context = {}
|
||||
return render_to_response(
|
||||
"common/%s.html" % errortpl, user_context,
|
||||
context_instance=template.RequestContext(request)
|
||||
return render(
|
||||
request, "common/%s.html" % errortpl, user_context
|
||||
)
|
||||
|
||||
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
{% extends "nlayout.html" %}
|
||||
{% extends "layout_simple.html" %}
|
||||
{% load i18n %}
|
||||
{% block content %}
|
||||
<div class="alert alert-error"><strong>{% trans "Error:" %}</strong> {{ error }}</div>
|
||||
|
||||
Reference in New Issue
Block a user