misc: apply double-quote-string-fixer (#80252)
This commit is contained in:
parent
6e5428362a
commit
b019f07d9e
|
@ -2,7 +2,7 @@
|
||||||
import os
|
import os
|
||||||
import sys
|
import sys
|
||||||
|
|
||||||
if __name__ == "__main__":
|
if __name__ == '__main__':
|
||||||
config_file = False
|
config_file = False
|
||||||
|
|
||||||
argv = sys.argv[1:]
|
argv = sys.argv[1:]
|
||||||
|
@ -13,7 +13,7 @@ if __name__ == "__main__":
|
||||||
if config_file:
|
if config_file:
|
||||||
os.environ['AUTHENTIC2_SETTINGS_FILE'] = config_file
|
os.environ['AUTHENTIC2_SETTINGS_FILE'] = config_file
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentic2.settings")
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'authentic2.settings')
|
||||||
|
|
||||||
from django.core.management import execute_from_command_line
|
from django.core.management import execute_from_command_line
|
||||||
|
|
||||||
|
|
|
@ -13,63 +13,63 @@ from optparse import OptionParser
|
||||||
# parse arguments
|
# parse arguments
|
||||||
newline = 10 * '\t'
|
newline = 10 * '\t'
|
||||||
parser = OptionParser(
|
parser = OptionParser(
|
||||||
usage="%prog [options] [file1 file2 ... filen]",
|
usage='%prog [options] [file1 file2 ... filen]',
|
||||||
version="%prog 1.0",
|
version='%prog 1.0',
|
||||||
epilog="If no files are specified all xml files in current directory will be selected. \n"
|
epilog='If no files are specified all xml files in current directory will be selected. \n'
|
||||||
+ "Useful when there is not known precise file name only location",
|
+ 'Useful when there is not known precise file name only location',
|
||||||
)
|
)
|
||||||
|
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-o",
|
'-o',
|
||||||
"--output",
|
'--output',
|
||||||
dest="filename",
|
dest='filename',
|
||||||
default="coverage-merged.xml",
|
default='coverage-merged.xml',
|
||||||
help="output file xml name",
|
help='output file xml name',
|
||||||
metavar="FILE",
|
metavar='FILE',
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-p", "--path", dest="path", default="./", help="xml location, default current directory", metavar="FILE"
|
'-p', '--path', dest='path', default='./', help='xml location, default current directory', metavar='FILE'
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-l", "--log", dest="loglevel", default="DEBUG", help="Log level DEBUG, INFO, WARNING, ERROR, CRITICAL"
|
'-l', '--log', dest='loglevel', default='DEBUG', help='Log level DEBUG, INFO, WARNING, ERROR, CRITICAL'
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-f",
|
'-f',
|
||||||
"--filteronly",
|
'--filteronly',
|
||||||
dest="filteronly",
|
dest='filteronly',
|
||||||
default=False,
|
default=False,
|
||||||
action='store_true',
|
action='store_true',
|
||||||
help="If set all files will be filtered by keep rules otherwise "
|
help='If set all files will be filtered by keep rules otherwise '
|
||||||
+ "all given files will be merged and filtered.",
|
+ 'all given files will be merged and filtered.',
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-s",
|
'-s',
|
||||||
"--suffix",
|
'--suffix',
|
||||||
dest="suffix",
|
dest='suffix',
|
||||||
default='',
|
default='',
|
||||||
help="Additional suffix which will be added to filtered files so they original files can be preserved",
|
help='Additional suffix which will be added to filtered files so they original files can be preserved',
|
||||||
)
|
)
|
||||||
parser.add_option(
|
parser.add_option(
|
||||||
"-k",
|
'-k',
|
||||||
"--keep",
|
'--keep',
|
||||||
dest="packagefilters",
|
dest='packagefilters',
|
||||||
default=None,
|
default=None,
|
||||||
metavar="NAME",
|
metavar='NAME',
|
||||||
action="append",
|
action='append',
|
||||||
help="preserves only specific packages. e.g.: "
|
help='preserves only specific packages. e.g.: '
|
||||||
+ newline
|
+ newline
|
||||||
+ "'python merge.py -k src.la.*'"
|
+ "'python merge.py -k src.la.*'"
|
||||||
+ newline
|
+ newline
|
||||||
+ "will keep all packgages in folder "
|
+ 'will keep all packgages in folder '
|
||||||
+ "src/la/ and all subfolders of this folders. "
|
+ 'src/la/ and all subfolders of this folders. '
|
||||||
+ newline
|
+ newline
|
||||||
+ "There can be mutiple rules e.g.:"
|
+ 'There can be mutiple rules e.g.:'
|
||||||
+ newline
|
+ newline
|
||||||
+ "'python merge.py -k src.la.* -k unit_tests.la.'"
|
+ "'python merge.py -k src.la.* -k unit_tests.la.'"
|
||||||
+ newline
|
+ newline
|
||||||
+ "Format of the rule is simple dot (.) separated names with wildcard (*) allowed, e.g: "
|
+ 'Format of the rule is simple dot (.) separated names with wildcard (*) allowed, e.g: '
|
||||||
+ newline
|
+ newline
|
||||||
+ "package.subpackage.*",
|
+ 'package.subpackage.*',
|
||||||
)
|
)
|
||||||
(options, args) = parser.parse_args()
|
(options, args) = parser.parse_args()
|
||||||
|
|
||||||
|
@ -129,7 +129,7 @@ def merge_xml(xmlfile1, xmlfile2, outputfile):
|
||||||
merge(packages1root, packages1, packages2, 'name', merge_packages)
|
merge(packages1root, packages1, packages2, 'name', merge_packages)
|
||||||
|
|
||||||
# write result to output file
|
# write result to output file
|
||||||
xml1.write(outputfile, encoding="UTF-8", xml_declaration=True)
|
xml1.write(outputfile, encoding='UTF-8', xml_declaration=True)
|
||||||
|
|
||||||
|
|
||||||
def filter_xml(xmlfile):
|
def filter_xml(xmlfile):
|
||||||
|
@ -265,7 +265,7 @@ if filteronly:
|
||||||
xml = ET.parse(xmlfile)
|
xml = ET.parse(xmlfile)
|
||||||
filter_xml(xml)
|
filter_xml(xml)
|
||||||
logging.debug(f'{currfile}/{totalfiles} filtering: {xmlfile}')
|
logging.debug(f'{currfile}/{totalfiles} filtering: {xmlfile}')
|
||||||
xml.write(xmlfile + filtersuffix, encoding="UTF-8", xml_declaration=True)
|
xml.write(xmlfile + filtersuffix, encoding='UTF-8', xml_declaration=True)
|
||||||
currfile += 1
|
currfile += 1
|
||||||
else:
|
else:
|
||||||
# merge all given files
|
# merge all given files
|
||||||
|
@ -278,7 +278,7 @@ else:
|
||||||
xmlfile = xmlfiles.pop(0)
|
xmlfile = xmlfiles.pop(0)
|
||||||
xml = ET.parse(xmlfile)
|
xml = ET.parse(xmlfile)
|
||||||
filter_xml(xml)
|
filter_xml(xml)
|
||||||
xml.write(finalxml, encoding="UTF-8", xml_declaration=True)
|
xml.write(finalxml, encoding='UTF-8', xml_declaration=True)
|
||||||
sys.exit(0)
|
sys.exit(0)
|
||||||
|
|
||||||
currfile = 1
|
currfile = 1
|
||||||
|
|
32
setup.py
32
setup.py
|
@ -99,7 +99,7 @@ class sdist(_sdist):
|
||||||
sub_commands = [('compile_translations', None)] + _sdist.sub_commands
|
sub_commands = [('compile_translations', None)] + _sdist.sub_commands
|
||||||
|
|
||||||
def run(self):
|
def run(self):
|
||||||
print("creating VERSION file")
|
print('creating VERSION file')
|
||||||
if os.path.exists('VERSION'):
|
if os.path.exists('VERSION'):
|
||||||
os.remove('VERSION')
|
os.remove('VERSION')
|
||||||
version = get_version()
|
version = get_version()
|
||||||
|
@ -107,7 +107,7 @@ class sdist(_sdist):
|
||||||
version_file.write(version)
|
version_file.write(version)
|
||||||
version_file.close()
|
version_file.close()
|
||||||
_sdist.run(self)
|
_sdist.run(self)
|
||||||
print("removing VERSION file")
|
print('removing VERSION file')
|
||||||
if os.path.exists('VERSION'):
|
if os.path.exists('VERSION'):
|
||||||
os.remove('VERSION')
|
os.remove('VERSION')
|
||||||
|
|
||||||
|
@ -146,15 +146,15 @@ def get_version():
|
||||||
|
|
||||||
|
|
||||||
setup(
|
setup(
|
||||||
name="authentic2",
|
name='authentic2',
|
||||||
version=get_version(),
|
version=get_version(),
|
||||||
license="AGPLv3+",
|
license='AGPLv3+',
|
||||||
description="Authentic 2, a versatile identity management server",
|
description='Authentic 2, a versatile identity management server',
|
||||||
url="http://dev.entrouvert.org/projects/authentic/",
|
url='http://dev.entrouvert.org/projects/authentic/',
|
||||||
author="Entr'ouvert",
|
author="Entr'ouvert",
|
||||||
author_email="authentic@listes.entrouvert.com",
|
author_email='authentic@listes.entrouvert.com',
|
||||||
maintainer="Benjamin Dauvergne",
|
maintainer='Benjamin Dauvergne',
|
||||||
maintainer_email="bdauvergne@entrouvert.com",
|
maintainer_email='bdauvergne@entrouvert.com',
|
||||||
scripts=('manage.py',),
|
scripts=('manage.py',),
|
||||||
packages=find_packages('src'),
|
packages=find_packages('src'),
|
||||||
package_dir={
|
package_dir={
|
||||||
|
@ -193,9 +193,9 @@ setup(
|
||||||
],
|
],
|
||||||
zip_safe=False,
|
zip_safe=False,
|
||||||
classifiers=[
|
classifiers=[
|
||||||
"Development Status :: 5 - Production/Stable",
|
'Development Status :: 5 - Production/Stable',
|
||||||
"Environment :: Web Environment",
|
'Environment :: Web Environment',
|
||||||
"Framework :: Django",
|
'Framework :: Django',
|
||||||
'Intended Audience :: End Users/Desktop',
|
'Intended Audience :: End Users/Desktop',
|
||||||
'Intended Audience :: Developers',
|
'Intended Audience :: Developers',
|
||||||
'Intended Audience :: System Administrators',
|
'Intended Audience :: System Administrators',
|
||||||
|
@ -203,10 +203,10 @@ setup(
|
||||||
'Intended Audience :: Legal Industry',
|
'Intended Audience :: Legal Industry',
|
||||||
'Intended Audience :: Science/Research',
|
'Intended Audience :: Science/Research',
|
||||||
'Intended Audience :: Telecommunications Industry',
|
'Intended Audience :: Telecommunications Industry',
|
||||||
"License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)",
|
'License :: OSI Approved :: GNU Affero General Public License v3 or later (AGPLv3+)',
|
||||||
"Operating System :: OS Independent",
|
'Operating System :: OS Independent',
|
||||||
"Programming Language :: Python",
|
'Programming Language :: Python',
|
||||||
"Topic :: System :: Systems Administration :: Authentication/Directory",
|
'Topic :: System :: Systems Administration :: Authentication/Directory',
|
||||||
],
|
],
|
||||||
cmdclass={
|
cmdclass={
|
||||||
'build': build,
|
'build': build,
|
||||||
|
|
|
@ -53,7 +53,7 @@ class RoleAdmin(admin.ModelAdmin):
|
||||||
'service',
|
'service',
|
||||||
)
|
)
|
||||||
readonly_fields = ('uuid',)
|
readonly_fields = ('uuid',)
|
||||||
prepopulated_fields = {"slug": ("name",)}
|
prepopulated_fields = {'slug': ('name',)}
|
||||||
filter_horizontal = ('members', 'permissions')
|
filter_horizontal = ('members', 'permissions')
|
||||||
list_display = ('__str__', 'slug', 'ou', 'service', 'admin_scope')
|
list_display = ('__str__', 'slug', 'ou', 'service', 'admin_scope')
|
||||||
list_select_related = True
|
list_select_related = True
|
||||||
|
@ -77,7 +77,7 @@ class OrganizationalUnitAdmin(admin.ModelAdmin):
|
||||||
'colour',
|
'colour',
|
||||||
)
|
)
|
||||||
readonly_fields = ('uuid',)
|
readonly_fields = ('uuid',)
|
||||||
prepopulated_fields = {"slug": ("name",)}
|
prepopulated_fields = {'slug': ('name',)}
|
||||||
list_display = ('name', 'slug')
|
list_display = ('name', 'slug')
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -11,7 +11,7 @@ class PermAnyLookupDict:
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
# I am large, I contain multitudes.
|
# I am large, I contain multitudes.
|
||||||
raise TypeError("PermAnyLookupDict is not iterable.")
|
raise TypeError('PermAnyLookupDict is not iterable.')
|
||||||
|
|
||||||
def __getitem__(self, perm_name):
|
def __getitem__(self, perm_name):
|
||||||
return self.user.has_perm_any('%s.%s' % (self.app_label, perm_name))
|
return self.user.has_perm_any('%s.%s' % (self.app_label, perm_name))
|
||||||
|
@ -29,7 +29,7 @@ class PermAnyWrapper:
|
||||||
|
|
||||||
def __iter__(self):
|
def __iter__(self):
|
||||||
# I am large, I contain multitudes.
|
# I am large, I contain multitudes.
|
||||||
raise TypeError("PermAnyWrapper is not iterable.")
|
raise TypeError('PermAnyWrapper is not iterable.')
|
||||||
|
|
||||||
def __bool__(self):
|
def __bool__(self):
|
||||||
raise TypeError('PermAnyWrapper has not boolean value')
|
raise TypeError('PermAnyWrapper has not boolean value')
|
||||||
|
|
|
@ -87,9 +87,9 @@ class OrganizationalUnit(AbstractBase):
|
||||||
)
|
)
|
||||||
|
|
||||||
USER_CAN_RESET_PASSWD_CHOICES = (
|
USER_CAN_RESET_PASSWD_CHOICES = (
|
||||||
(None, _("System default")),
|
(None, _('System default')),
|
||||||
(True, _("Yes")),
|
(True, _('Yes')),
|
||||||
(False, _("No")),
|
(False, _('No')),
|
||||||
)
|
)
|
||||||
|
|
||||||
PolicyValue = namedtuple(
|
PolicyValue = namedtuple(
|
||||||
|
@ -292,10 +292,10 @@ class Permission(models.Model):
|
||||||
|
|
||||||
def export_json(self):
|
def export_json(self):
|
||||||
return {
|
return {
|
||||||
"operation": self.operation.natural_key_json(),
|
'operation': self.operation.natural_key_json(),
|
||||||
"ou": self.ou and self.ou.natural_key_json(),
|
'ou': self.ou and self.ou.natural_key_json(),
|
||||||
'target_ct': self.target_ct.natural_key_json(),
|
'target_ct': self.target_ct.natural_key_json(),
|
||||||
"target": self.target.natural_key_json(),
|
'target': self.target.natural_key_json(),
|
||||||
}
|
}
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
|
|
@ -51,7 +51,7 @@ class CleanupAdminMixin(admin.ModelAdmin):
|
||||||
|
|
||||||
@admin.register(Nonce)
|
@admin.register(Nonce)
|
||||||
class NonceModelAdmin(admin.ModelAdmin):
|
class NonceModelAdmin(admin.ModelAdmin):
|
||||||
list_display = ("value", "context", "not_on_or_after")
|
list_display = ('value', 'context', 'not_on_or_after')
|
||||||
|
|
||||||
|
|
||||||
class AttributeValueAdmin(admin.ModelAdmin):
|
class AttributeValueAdmin(admin.ModelAdmin):
|
||||||
|
@ -190,11 +190,11 @@ class UserRealmListFilter(admin.SimpleListFilter):
|
||||||
|
|
||||||
class UserChangeForm(BaseUserForm):
|
class UserChangeForm(BaseUserForm):
|
||||||
error_messages = {
|
error_messages = {
|
||||||
'missing_credential': _("You must at least give a username or an email to your user"),
|
'missing_credential': _('You must at least give a username or an email to your user'),
|
||||||
}
|
}
|
||||||
|
|
||||||
password = ReadOnlyPasswordHashField(
|
password = ReadOnlyPasswordHashField(
|
||||||
label=_("Password"),
|
label=_('Password'),
|
||||||
help_text=_(
|
help_text=_(
|
||||||
"Raw passwords are not stored, so there is no way to see this user's password, but you can change"
|
"Raw passwords are not stored, so there is no way to see this user's password, but you can change"
|
||||||
" the password using <a href=\"password/\">this form</a>."
|
" the password using <a href=\"password/\">this form</a>."
|
||||||
|
@ -215,7 +215,7 @@ class UserChangeForm(BaseUserForm):
|
||||||
# Regardless of what the user provides, return the initial value.
|
# Regardless of what the user provides, return the initial value.
|
||||||
# This is done here, rather than on the field, because the
|
# This is done here, rather than on the field, because the
|
||||||
# field does not have access to the initial value
|
# field does not have access to the initial value
|
||||||
return self.initial["password"]
|
return self.initial['password']
|
||||||
|
|
||||||
def clean(self):
|
def clean(self):
|
||||||
if not self.cleaned_data.get('username') and not self.cleaned_data.get('email'):
|
if not self.cleaned_data.get('username') and not self.cleaned_data.get('email'):
|
||||||
|
@ -236,22 +236,22 @@ class UserCreationForm(BaseUserForm):
|
||||||
|
|
||||||
error_messages = {
|
error_messages = {
|
||||||
'password_mismatch': _("The two password fields didn't match."),
|
'password_mismatch': _("The two password fields didn't match."),
|
||||||
'missing_credential': _("You must at least give a username or an email to your user"),
|
'missing_credential': _('You must at least give a username or an email to your user'),
|
||||||
}
|
}
|
||||||
password1 = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
|
password1 = forms.CharField(label=_('Password'), widget=forms.PasswordInput)
|
||||||
password2 = forms.CharField(
|
password2 = forms.CharField(
|
||||||
label=_("Password confirmation"),
|
label=_('Password confirmation'),
|
||||||
widget=forms.PasswordInput,
|
widget=forms.PasswordInput,
|
||||||
help_text=_("Enter the same password as above, for verification."),
|
help_text=_('Enter the same password as above, for verification.'),
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
model = User
|
model = User
|
||||||
fields = ("username",)
|
fields = ('username',)
|
||||||
|
|
||||||
def clean_password2(self):
|
def clean_password2(self):
|
||||||
password1 = self.cleaned_data.get("password1")
|
password1 = self.cleaned_data.get('password1')
|
||||||
password2 = self.cleaned_data.get("password2")
|
password2 = self.cleaned_data.get('password2')
|
||||||
if password1 and password2 and password1 != password2:
|
if password1 and password2 and password1 != password2:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
self.error_messages['password_mismatch'],
|
self.error_messages['password_mismatch'],
|
||||||
|
@ -268,7 +268,7 @@ class UserCreationForm(BaseUserForm):
|
||||||
|
|
||||||
def save(self, commit=True):
|
def save(self, commit=True):
|
||||||
user = super().save(commit=False)
|
user = super().save(commit=False)
|
||||||
user.set_password(self.cleaned_data["password1"])
|
user.set_password(self.cleaned_data['password1'])
|
||||||
if commit:
|
if commit:
|
||||||
user.save()
|
user.save()
|
||||||
return user
|
return user
|
||||||
|
|
|
@ -528,12 +528,12 @@ class BaseUserSerializer(serializers.ModelSerializer):
|
||||||
try:
|
try:
|
||||||
hasher = identify_hasher(attrs.get('hashed_password'))
|
hasher = identify_hasher(attrs.get('hashed_password'))
|
||||||
except ValueError:
|
except ValueError:
|
||||||
errors['hashed_password'] = "unknown hash format"
|
errors['hashed_password'] = 'unknown hash format'
|
||||||
else:
|
else:
|
||||||
try:
|
try:
|
||||||
hasher.safe_summary(attrs.get('hashed_password'))
|
hasher.safe_summary(attrs.get('hashed_password'))
|
||||||
except Exception:
|
except Exception:
|
||||||
errors['hashed_password'] = "hash format error"
|
errors['hashed_password'] = 'hash format error'
|
||||||
if errors:
|
if errors:
|
||||||
raise serializers.ValidationError(errors)
|
raise serializers.ValidationError(errors)
|
||||||
return attrs
|
return attrs
|
||||||
|
@ -779,7 +779,7 @@ class UsersAPI(api_mixins.GetOrCreateMixinView, HookMixin, ExceptionHandlerMixin
|
||||||
if self.request.user.has_ou_perm(perm, ou):
|
if self.request.user.has_ou_perm(perm, ou):
|
||||||
allowed_ous.append(ou)
|
allowed_ous.append(ou)
|
||||||
if not allowed_ous:
|
if not allowed_ous:
|
||||||
raise PermissionDenied("You do not have permission to perform this action.")
|
raise PermissionDenied('You do not have permission to perform this action.')
|
||||||
|
|
||||||
queryset = queryset.filter(ou__in=allowed_ous)
|
queryset = queryset.filter(ou__in=allowed_ous)
|
||||||
return queryset
|
return queryset
|
||||||
|
|
|
@ -40,10 +40,10 @@ class Command(BaseCommand):
|
||||||
help='Specifies the database to use. Default is "default".',
|
help='Specifies the database to use. Default is "default".',
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_pass(self, prompt="Password: "):
|
def _get_pass(self, prompt='Password: '):
|
||||||
p = getpass.getpass(prompt=force_str(prompt))
|
p = getpass.getpass(prompt=force_str(prompt))
|
||||||
if not p:
|
if not p:
|
||||||
raise CommandError("aborted")
|
raise CommandError('aborted')
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
|
@ -87,9 +87,9 @@ class Command(BaseCommand):
|
||||||
p1, p2 = 1, 2 # To make them initially mismatch.
|
p1, p2 = 1, 2 # To make them initially mismatch.
|
||||||
while p1 != p2 and count < MAX_TRIES:
|
while p1 != p2 and count < MAX_TRIES:
|
||||||
p1 = self._get_pass()
|
p1 = self._get_pass()
|
||||||
p2 = self._get_pass("Password (again): ")
|
p2 = self._get_pass('Password (again): ')
|
||||||
if p1 != p2:
|
if p1 != p2:
|
||||||
self.stdout.write("Passwords do not match. Please try again.\n")
|
self.stdout.write('Passwords do not match. Please try again.\n')
|
||||||
count = count + 1
|
count = count + 1
|
||||||
|
|
||||||
if count == MAX_TRIES:
|
if count == MAX_TRIES:
|
||||||
|
|
|
@ -21,7 +21,7 @@ from authentic2.custom_user.models import User
|
||||||
|
|
||||||
|
|
||||||
class Command(BaseCommand):
|
class Command(BaseCommand):
|
||||||
help = "Fix user attributes"
|
help = 'Fix user attributes'
|
||||||
|
|
||||||
requires_system_checks = []
|
requires_system_checks = []
|
||||||
|
|
||||||
|
|
|
@ -172,7 +172,7 @@ class UserQuerySet(models.QuerySet):
|
||||||
def set_trigram_similarity_threshold(self, threshold=None):
|
def set_trigram_similarity_threshold(self, threshold=None):
|
||||||
with connection.cursor() as cursor:
|
with connection.cursor() as cursor:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"SET pg_trgm.similarity_threshold = %f" % (threshold or app_settings.A2_FTS_THRESHOLD)
|
'SET pg_trgm.similarity_threshold = %f' % (threshold or app_settings.A2_FTS_THRESHOLD)
|
||||||
)
|
)
|
||||||
|
|
||||||
def get_by_email(self, email):
|
def get_by_email(self, email):
|
||||||
|
|
|
@ -60,8 +60,8 @@ class Migration(migrations.Migration):
|
||||||
TrigramExtension(),
|
TrigramExtension(),
|
||||||
RunSQLIfExtension(
|
RunSQLIfExtension(
|
||||||
sql=[
|
sql=[
|
||||||
"CREATE INDEX IF NOT EXISTS custom_user_user_email_trgm_idx ON custom_user_user USING gist"
|
'CREATE INDEX IF NOT EXISTS custom_user_user_email_trgm_idx ON custom_user_user USING gist'
|
||||||
" (LOWER(email) public.gist_trgm_ops)"
|
' (LOWER(email) public.gist_trgm_ops)'
|
||||||
],
|
],
|
||||||
reverse_sql=['DROP INDEX custom_user_user_email_trgm_idx'],
|
reverse_sql=['DROP INDEX custom_user_user_email_trgm_idx'],
|
||||||
),
|
),
|
||||||
|
|
|
@ -230,16 +230,16 @@ class User(AbstractBaseUser):
|
||||||
'The groups this user belongs to. A user will get all permissions granted to each of his/her'
|
'The groups this user belongs to. A user will get all permissions granted to each of his/her'
|
||||||
' group.'
|
' group.'
|
||||||
),
|
),
|
||||||
related_name="user_set",
|
related_name='user_set',
|
||||||
related_query_name="user",
|
related_query_name='user',
|
||||||
)
|
)
|
||||||
user_permissions = models.ManyToManyField(
|
user_permissions = models.ManyToManyField(
|
||||||
to=AuthPermission,
|
to=AuthPermission,
|
||||||
verbose_name=_('user permissions'),
|
verbose_name=_('user permissions'),
|
||||||
blank=True,
|
blank=True,
|
||||||
help_text=_('Specific permissions for this user.'),
|
help_text=_('Specific permissions for this user.'),
|
||||||
related_name="user_set",
|
related_name='user_set',
|
||||||
related_query_name="user",
|
related_query_name='user',
|
||||||
)
|
)
|
||||||
|
|
||||||
# events dates
|
# events dates
|
||||||
|
@ -277,7 +277,7 @@ class User(AbstractBaseUser):
|
||||||
"""
|
"""
|
||||||
permissions = set()
|
permissions = set()
|
||||||
for backend in auth.get_backends():
|
for backend in auth.get_backends():
|
||||||
if hasattr(backend, "get_group_permissions"):
|
if hasattr(backend, 'get_group_permissions'):
|
||||||
permissions.update(backend.get_group_permissions(self, obj))
|
permissions.update(backend.get_group_permissions(self, obj))
|
||||||
return permissions
|
return permissions
|
||||||
|
|
||||||
|
@ -329,7 +329,7 @@ class User(AbstractBaseUser):
|
||||||
def filter_by_perm(self, perm_or_perms, qs):
|
def filter_by_perm(self, perm_or_perms, qs):
|
||||||
results = []
|
results = []
|
||||||
for backend in auth.get_backends():
|
for backend in auth.get_backends():
|
||||||
if hasattr(backend, "filter_by_perm"):
|
if hasattr(backend, 'filter_by_perm'):
|
||||||
results.append(backend.filter_by_perm(self, perm_or_perms, qs))
|
results.append(backend.filter_by_perm(self, perm_or_perms, qs))
|
||||||
if results:
|
if results:
|
||||||
return functools.reduce(operator.__or__, results)
|
return functools.reduce(operator.__or__, results)
|
||||||
|
@ -342,7 +342,7 @@ class User(AbstractBaseUser):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
for backend in auth.get_backends():
|
for backend in auth.get_backends():
|
||||||
if hasattr(backend, "has_perm_any"):
|
if hasattr(backend, 'has_perm_any'):
|
||||||
if backend.has_perm_any(self, perm_or_perms):
|
if backend.has_perm_any(self, perm_or_perms):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -353,7 +353,7 @@ class User(AbstractBaseUser):
|
||||||
return True
|
return True
|
||||||
|
|
||||||
for backend in auth.get_backends():
|
for backend in auth.get_backends():
|
||||||
if hasattr(backend, "has_ou_perm"):
|
if hasattr(backend, 'has_ou_perm'):
|
||||||
if backend.has_ou_perm(self, perm, ou):
|
if backend.has_ou_perm(self, perm, ou):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
@ -369,7 +369,7 @@ class User(AbstractBaseUser):
|
||||||
return full_name.strip() or self.username or self.email
|
return full_name.strip() or self.username or self.email
|
||||||
|
|
||||||
def get_short_name(self):
|
def get_short_name(self):
|
||||||
"Returns the short name for the user."
|
'Returns the short name for the user.'
|
||||||
return self.first_name or self.username or self.email or self.uuid[:6]
|
return self.first_name or self.username or self.email or self.uuid[:6]
|
||||||
|
|
||||||
def email_user(self, subject, message, from_email=None):
|
def email_user(self, subject, message, from_email=None):
|
||||||
|
@ -379,7 +379,7 @@ class User(AbstractBaseUser):
|
||||||
send_mail(subject, message, from_email, [self.email])
|
send_mail(subject, message, from_email, [self.email])
|
||||||
|
|
||||||
def get_username(self):
|
def get_username(self):
|
||||||
"Return the identifying username for this User"
|
'Return the identifying username for this User'
|
||||||
return self.username or self.email or self.get_full_name() or self.uuid
|
return self.username or self.email or self.get_full_name() or self.uuid
|
||||||
|
|
||||||
def roles_and_parents(self):
|
def roles_and_parents(self):
|
||||||
|
|
|
@ -228,7 +228,7 @@ class RoleDeserializer:
|
||||||
ou = get_default_ou()
|
ou = get_default_ou()
|
||||||
else:
|
else:
|
||||||
name = self._role_d.get('name') or self._role_d.get('slug') or self._role_d.get('uuid')
|
name = self._role_d.get('name') or self._role_d.get('slug') or self._role_d.get('uuid')
|
||||||
raise ValidationError(_("Missing Organizational Unit for role: %s") % name)
|
raise ValidationError(_('Missing Organizational Unit for role: %s') % name)
|
||||||
|
|
||||||
obj = search_role(self._role_d, ou=self._import_context.set_ou)
|
obj = search_role(self._role_d, ou=self._import_context.set_ou)
|
||||||
|
|
||||||
|
@ -292,7 +292,7 @@ class RoleDeserializer:
|
||||||
for parent_d in self._parents:
|
for parent_d in self._parents:
|
||||||
parent = search_role(parent_d)
|
parent = search_role(parent_d)
|
||||||
if not parent:
|
if not parent:
|
||||||
raise ValidationError(_("Could not find parent role: %s") % parent_d)
|
raise ValidationError(_('Could not find parent role: %s') % parent_d)
|
||||||
created.append(RoleParenting.objects.create(child=self._obj, direct=True, parent=parent))
|
created.append(RoleParenting.objects.create(child=self._obj, direct=True, parent=parent))
|
||||||
|
|
||||||
return created, deleted
|
return created, deleted
|
||||||
|
@ -347,13 +347,13 @@ class ImportResult:
|
||||||
self._bulk_update('permissions', created, deleted)
|
self._bulk_update('permissions', created, deleted)
|
||||||
|
|
||||||
def to_str(self, verbose=False):
|
def to_str(self, verbose=False):
|
||||||
res = ""
|
res = ''
|
||||||
for attr in ('roles', 'ous', 'parentings', 'permissions', 'attributes'):
|
for attr in ('roles', 'ous', 'parentings', 'permissions', 'attributes'):
|
||||||
data = getattr(self, attr)
|
data = getattr(self, attr)
|
||||||
for status in ('created', 'updated', 'deleted'):
|
for status in ('created', 'updated', 'deleted'):
|
||||||
if status in data:
|
if status in data:
|
||||||
s_data = data[status]
|
s_data = data[status]
|
||||||
res += "%s %s %s\n" % (len(s_data), attr, status)
|
res += '%s %s %s\n' % (len(s_data), attr, status)
|
||||||
return res
|
return res
|
||||||
|
|
||||||
|
|
||||||
|
@ -408,13 +408,13 @@ def import_site(json_d, import_context=None):
|
||||||
|
|
||||||
if import_context.ou_delete_orphans:
|
if import_context.ou_delete_orphans:
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("Unsupported context value for ou_delete_orphans : %s") % (import_context.ou_delete_orphans)
|
_('Unsupported context value for ou_delete_orphans : %s') % (import_context.ou_delete_orphans)
|
||||||
)
|
)
|
||||||
|
|
||||||
if import_context.role_delete_orphans:
|
if import_context.role_delete_orphans:
|
||||||
# FIXME : delete each role that is in DB but not in the export
|
# FIXME : delete each role that is in DB but not in the export
|
||||||
raise ValidationError(
|
raise ValidationError(
|
||||||
_("Unsupported context value for role_delete_orphans : %s")
|
_('Unsupported context value for role_delete_orphans : %s')
|
||||||
% (import_context.role_delete_orphans)
|
% (import_context.role_delete_orphans)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -99,7 +99,7 @@ def get_disco_return_url_from_metadata(entity_id):
|
||||||
value = int(endpoint.attributes['index'].value)
|
value = int(endpoint.attributes['index'].value)
|
||||||
ep = endpoint
|
ep = endpoint
|
||||||
if not ep:
|
if not ep:
|
||||||
logger.warning("get_disco_return_url_from_metadata: no valid endpoint for %s", entity_id)
|
logger.warning('get_disco_return_url_from_metadata: no valid endpoint for %s', entity_id)
|
||||||
return None
|
return None
|
||||||
|
|
||||||
logger.debug('get_disco_return_url_from_metadata: found endpoint with index %s', value)
|
logger.debug('get_disco_return_url_from_metadata: found endpoint with index %s', value)
|
||||||
|
@ -132,13 +132,13 @@ def add_param_to_url(url, param_name, value):
|
||||||
|
|
||||||
|
|
||||||
def disco(request):
|
def disco(request):
|
||||||
if not request.method == "GET":
|
if not request.method == 'GET':
|
||||||
message = _('HTTP verb not supported %s' % request.method)
|
message = _('HTTP verb not supported %s' % request.method)
|
||||||
return error_page(request, message, logger=logger)
|
return error_page(request, message, logger=logger)
|
||||||
|
|
||||||
entityID = None
|
entityID = None
|
||||||
_return = None
|
_return = None
|
||||||
policy = ("urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol:single",)
|
policy = ('urn:oasis:names:tc:SAML:profiles:SSO:idp-discovery-protocol:single',)
|
||||||
returnIDParam = None
|
returnIDParam = None
|
||||||
isPassive = False
|
isPassive = False
|
||||||
|
|
||||||
|
@ -151,7 +151,7 @@ def disco(request):
|
||||||
|
|
||||||
if not is_known_idp(idp_selected):
|
if not is_known_idp(idp_selected):
|
||||||
message = 'The idp is unknown.'
|
message = 'The idp is unknown.'
|
||||||
logger.warning("disco: Unknown selected idp %s", idp_selected)
|
logger.warning('disco: Unknown selected idp %s', idp_selected)
|
||||||
save_key_values(request, entityID, _return, policy, returnIDParam, isPassive)
|
save_key_values(request, entityID, _return, policy, returnIDParam, isPassive)
|
||||||
return HttpResponseRedirect(reverse(idp_selection))
|
return HttpResponseRedirect(reverse(idp_selection))
|
||||||
|
|
||||||
|
|
|
@ -44,7 +44,7 @@ logger = logging.getLogger(__name__)
|
||||||
class PasswordResetForm(HoneypotForm):
|
class PasswordResetForm(HoneypotForm):
|
||||||
next_url = forms.CharField(widget=forms.HiddenInput, required=False)
|
next_url = forms.CharField(widget=forms.HiddenInput, required=False)
|
||||||
|
|
||||||
email = ValidatedEmailField(label=_("Email"), required=False)
|
email = ValidatedEmailField(label=_('Email'), required=False)
|
||||||
|
|
||||||
phone = PhoneField(
|
phone = PhoneField(
|
||||||
label=_('Phone number'),
|
label=_('Phone number'),
|
||||||
|
@ -246,13 +246,13 @@ class NotifyOfPasswordChange:
|
||||||
'user': user,
|
'user': user,
|
||||||
'password': self.cleaned_data['new_password1'],
|
'password': self.cleaned_data['new_password1'],
|
||||||
}
|
}
|
||||||
utils_misc.send_templated_mail(user, "authentic2/password_change", ctx)
|
utils_misc.send_templated_mail(user, 'authentic2/password_change', ctx)
|
||||||
return user
|
return user
|
||||||
|
|
||||||
|
|
||||||
class SetPasswordForm(NotifyOfPasswordChange, PasswordResetMixin, auth_forms.SetPasswordForm):
|
class SetPasswordForm(NotifyOfPasswordChange, PasswordResetMixin, auth_forms.SetPasswordForm):
|
||||||
new_password1 = NewPasswordField(label=_("New password"))
|
new_password1 = NewPasswordField(label=_('New password'))
|
||||||
new_password2 = CheckPasswordField(label=_("New password confirmation"))
|
new_password2 = CheckPasswordField(label=_('New password confirmation'))
|
||||||
|
|
||||||
def __init__(self, user, *args, **kwargs):
|
def __init__(self, user, *args, **kwargs):
|
||||||
super().__init__(user, *args, **kwargs)
|
super().__init__(user, *args, **kwargs)
|
||||||
|
@ -271,7 +271,7 @@ class PasswordChangeForm(
|
||||||
):
|
):
|
||||||
old_password = PasswordField(label=_('Old password'))
|
old_password = PasswordField(label=_('Old password'))
|
||||||
new_password1 = NewPasswordField(label=_('New password'))
|
new_password1 = NewPasswordField(label=_('New password'))
|
||||||
new_password2 = CheckPasswordField(label=_("New password confirmation"))
|
new_password2 = CheckPasswordField(label=_('New password confirmation'))
|
||||||
|
|
||||||
old_password.widget.attrs.update({'autocomplete': 'current-password'})
|
old_password.widget.attrs.update({'autocomplete': 'current-password'})
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ class EmailChangeFormNoPassword(forms.Form):
|
||||||
|
|
||||||
|
|
||||||
class EmailChangeForm(EmailChangeFormNoPassword):
|
class EmailChangeForm(EmailChangeFormNoPassword):
|
||||||
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
|
password = forms.CharField(label=_('Password'), widget=forms.PasswordInput)
|
||||||
|
|
||||||
def clean_email(self):
|
def clean_email(self):
|
||||||
email = self.cleaned_data['email']
|
email = self.cleaned_data['email']
|
||||||
|
@ -45,7 +45,7 @@ class EmailChangeForm(EmailChangeFormNoPassword):
|
||||||
return email
|
return email
|
||||||
|
|
||||||
def clean_password(self):
|
def clean_password(self):
|
||||||
password = self.cleaned_data["password"]
|
password = self.cleaned_data['password']
|
||||||
if not self.user.check_password(password):
|
if not self.user.check_password(password):
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
_('Incorrect password.'),
|
_('Incorrect password.'),
|
||||||
|
@ -63,7 +63,7 @@ class PhoneChangeFormNoPassword(forms.Form):
|
||||||
|
|
||||||
|
|
||||||
class PhoneChangeForm(PhoneChangeFormNoPassword):
|
class PhoneChangeForm(PhoneChangeFormNoPassword):
|
||||||
password = forms.CharField(label=_("Password"), widget=forms.PasswordInput)
|
password = forms.CharField(label=_('Password'), widget=forms.PasswordInput)
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
self.authenticator = kwargs.pop('password_authenticator')
|
self.authenticator = kwargs.pop('password_authenticator')
|
||||||
|
@ -80,7 +80,7 @@ class PhoneChangeForm(PhoneChangeFormNoPassword):
|
||||||
return phone
|
return phone
|
||||||
|
|
||||||
def clean_password(self):
|
def clean_password(self):
|
||||||
password = self.cleaned_data["password"]
|
password = self.cleaned_data['password']
|
||||||
if not self.user.check_password(password):
|
if not self.user.check_password(password):
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
_('Incorrect password.'),
|
_('Incorrect password.'),
|
||||||
|
@ -91,7 +91,7 @@ class PhoneChangeForm(PhoneChangeFormNoPassword):
|
||||||
|
|
||||||
class BaseUserForm(LockedFieldFormMixin, forms.ModelForm):
|
class BaseUserForm(LockedFieldFormMixin, forms.ModelForm):
|
||||||
error_messages = {
|
error_messages = {
|
||||||
'duplicate_username': _("A user with that username already exists."),
|
'duplicate_username': _('A user with that username already exists.'),
|
||||||
}
|
}
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
|
|
|
@ -169,7 +169,7 @@ class RegistrationCompletionFormNoPassword(profile_forms.BaseUserForm):
|
||||||
|
|
||||||
class RegistrationCompletionForm(RegistrationCompletionFormNoPassword):
|
class RegistrationCompletionForm(RegistrationCompletionFormNoPassword):
|
||||||
password1 = NewPasswordField(label=_('Password'))
|
password1 = NewPasswordField(label=_('Password'))
|
||||||
password2 = CheckPasswordField(label=_("Password (again)"))
|
password2 = CheckPasswordField(label=_('Password (again)'))
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
super().__init__(*args, **kwargs)
|
super().__init__(*args, **kwargs)
|
||||||
|
|
|
@ -150,7 +150,7 @@ class PickerWidgetMixin:
|
||||||
def render(self, name, value, attrs=None, renderer=None):
|
def render(self, name, value, attrs=None, renderer=None):
|
||||||
attrs = attrs or {}
|
attrs = attrs or {}
|
||||||
final_attrs = self.build_attrs(attrs)
|
final_attrs = self.build_attrs(attrs)
|
||||||
final_attrs['class'] = "controls input-append date"
|
final_attrs['class'] = 'controls input-append date'
|
||||||
rendered_widget = super().render(name, value, attrs=final_attrs, renderer=renderer)
|
rendered_widget = super().render(name, value, attrs=final_attrs, renderer=renderer)
|
||||||
|
|
||||||
# if not set, autoclose have to be true.
|
# if not set, autoclose have to be true.
|
||||||
|
@ -159,9 +159,9 @@ class PickerWidgetMixin:
|
||||||
# Build javascript options out of python dictionary
|
# Build javascript options out of python dictionary
|
||||||
options_list = []
|
options_list = []
|
||||||
for key, value in iter(self.options.items()):
|
for key, value in iter(self.options.items()):
|
||||||
options_list.append("%s: %s" % (key, json.dumps(value)))
|
options_list.append('%s: %s' % (key, json.dumps(value)))
|
||||||
|
|
||||||
js_options = ",\n".join(options_list)
|
js_options = ',\n'.join(options_list)
|
||||||
|
|
||||||
# Use provided id or generate hex to avoid collisions in document
|
# Use provided id or generate hex to avoid collisions in document
|
||||||
id = final_attrs.get('id', uuid.uuid4().hex)
|
id = final_attrs.get('id', uuid.uuid4().hex)
|
||||||
|
@ -318,7 +318,7 @@ class CheckPasswordInput(PasswordInput):
|
||||||
|
|
||||||
|
|
||||||
class ProfileImageInput(ClearableFileInput):
|
class ProfileImageInput(ClearableFileInput):
|
||||||
template_name = "authentic2/profile_image_input.html"
|
template_name = 'authentic2/profile_image_input.html'
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
attrs = kwargs.pop('attrs', {})
|
attrs = kwargs.pop('attrs', {})
|
||||||
|
|
|
@ -32,7 +32,7 @@ class Drupal7PasswordHasher(hashers.BasePasswordHasher):
|
||||||
Secure password hashing using the algorithm used by Drupal 7 (recommended)
|
Secure password hashing using the algorithm used by Drupal 7 (recommended)
|
||||||
"""
|
"""
|
||||||
|
|
||||||
algorithm = "drupal7_sha512"
|
algorithm = 'drupal7_sha512'
|
||||||
iterations = 10000
|
iterations = 10000
|
||||||
digest = hashlib.sha512
|
digest = hashlib.sha512
|
||||||
alphabet = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
alphabet = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz'
|
||||||
|
@ -85,7 +85,7 @@ class Drupal7PasswordHasher(hashers.BasePasswordHasher):
|
||||||
password = password.encode()
|
password = password.encode()
|
||||||
for dummy in range(iterations + 1):
|
for dummy in range(iterations + 1):
|
||||||
h = self.digest(h + password).digest()
|
h = self.digest(h + password).digest()
|
||||||
return "%s$%d$%s$%s" % (self.algorithm, iterations, salt, self.b64encode(h)[:43])
|
return '%s$%d$%s$%s' % (self.algorithm, iterations, salt, self.b64encode(h)[:43])
|
||||||
|
|
||||||
def verify(self, password, encoded):
|
def verify(self, password, encoded):
|
||||||
algorithm, iterations, salt, dummy = encoded.split('$', 3)
|
algorithm, iterations, salt, dummy = encoded.split('$', 3)
|
||||||
|
@ -118,7 +118,7 @@ class CommonPasswordHasher(hashers.BasePasswordHasher):
|
||||||
assert password
|
assert password
|
||||||
assert '$' not in salt
|
assert '$' not in salt
|
||||||
hash = self.digest(force_bytes(salt + password)).hexdigest()
|
hash = self.digest(force_bytes(salt + password)).hexdigest()
|
||||||
return "%s$%s$%s" % (self.algorithm, salt, hash)
|
return '%s$%s$%s' % (self.algorithm, salt, hash)
|
||||||
|
|
||||||
def verify(self, password, encoded):
|
def verify(self, password, encoded):
|
||||||
algorithm, salt, dummy_hash = encoded.split('$', 2)
|
algorithm, salt, dummy_hash = encoded.split('$', 2)
|
||||||
|
@ -173,7 +173,7 @@ class OpenLDAPPasswordHasher(CommonPasswordHasher):
|
||||||
assert b'$' not in salt
|
assert b'$' not in salt
|
||||||
hash = self.digest(force_bytes(password + salt)).hexdigest()
|
hash = self.digest(force_bytes(password + salt)).hexdigest()
|
||||||
salt = force_str(hexlify(salt), encoding='ascii')
|
salt = force_str(hexlify(salt), encoding='ascii')
|
||||||
return "%s$%s$%s" % (self.algorithm, salt, hash)
|
return '%s$%s$%s' % (self.algorithm, salt, hash)
|
||||||
|
|
||||||
def verify(self, password, encoded):
|
def verify(self, password, encoded):
|
||||||
algorithm, salt, hash = encoded.split('$', 2)
|
algorithm, salt, hash = encoded.split('$', 2)
|
||||||
|
@ -224,7 +224,7 @@ class JoomlaPasswordHasher(CommonPasswordHasher):
|
||||||
assert b'$' not in salt
|
assert b'$' not in salt
|
||||||
hash = self.digest(force_bytes(password) + salt).hexdigest()
|
hash = self.digest(force_bytes(password) + salt).hexdigest()
|
||||||
salt = force_str(hexlify(force_bytes(salt)), encoding='ascii')
|
salt = force_str(hexlify(force_bytes(salt)), encoding='ascii')
|
||||||
return "%s$md5$%s$%s" % (self.algorithm, salt, hash)
|
return '%s$md5$%s$%s' % (self.algorithm, salt, hash)
|
||||||
|
|
||||||
def verify(self, password, encoded):
|
def verify(self, password, encoded):
|
||||||
algorithm, dummy_subalgo, salt, dummy_hash = encoded.split('$', 3)
|
algorithm, dummy_subalgo, salt, dummy_hash = encoded.split('$', 3)
|
||||||
|
@ -272,7 +272,7 @@ class PloneSHA1PasswordHasher(hashers.SHA1PasswordHasher):
|
||||||
Plone uses `password + salt`, Django has `salt + password`.
|
Plone uses `password + salt`, Django has `salt + password`.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
algorithm = "plonesha1"
|
algorithm = 'plonesha1'
|
||||||
_prefix = '{SSHA}'
|
_prefix = '{SSHA}'
|
||||||
|
|
||||||
def encode(self, password, salt):
|
def encode(self, password, salt):
|
||||||
|
@ -283,7 +283,7 @@ class PloneSHA1PasswordHasher(hashers.SHA1PasswordHasher):
|
||||||
salt = force_bytes(salt)
|
salt = force_bytes(salt)
|
||||||
|
|
||||||
hashed = base64.b64encode(hashlib.sha1(password + salt).digest() + salt)
|
hashed = base64.b64encode(hashlib.sha1(password + salt).digest() + salt)
|
||||||
return "%s$%s%s" % (self.algorithm, self._prefix, force_str(hashed))
|
return '%s$%s%s' % (self.algorithm, self._prefix, force_str(hashed))
|
||||||
|
|
||||||
def verify(self, password, encoded):
|
def verify(self, password, encoded):
|
||||||
"""Verify the given password against the encoded string."""
|
"""Verify the given password against the encoded string."""
|
||||||
|
|
|
@ -23,7 +23,7 @@ from django.shortcuts import render
|
||||||
def consent_federation(request, nonce='', provider_id=None):
|
def consent_federation(request, nonce='', provider_id=None):
|
||||||
"""On a GET produce a form asking for consentment,
|
"""On a GET produce a form asking for consentment,
|
||||||
On a POST handle the form and redirect to next"""
|
On a POST handle the form and redirect to next"""
|
||||||
if request.method == "GET":
|
if request.method == 'GET':
|
||||||
return render(
|
return render(
|
||||||
request,
|
request,
|
||||||
'interaction/consent_federation.html',
|
'interaction/consent_federation.html',
|
||||||
|
|
|
@ -97,9 +97,9 @@ class SamlBackend:
|
||||||
|
|
||||||
def logout_list(self, request):
|
def logout_list(self, request):
|
||||||
all_sessions = models.LibertySession.objects.filter(django_session_key=request.session.session_key)
|
all_sessions = models.LibertySession.objects.filter(django_session_key=request.session.session_key)
|
||||||
self.logger.debug("all_sessions %r", all_sessions)
|
self.logger.debug('all_sessions %r', all_sessions)
|
||||||
provider_ids = {s.provider_id for s in all_sessions}
|
provider_ids = {s.provider_id for s in all_sessions}
|
||||||
self.logger.debug("provider_ids %r", provider_ids)
|
self.logger.debug('provider_ids %r', provider_ids)
|
||||||
result = []
|
result = []
|
||||||
for provider_id in provider_ids:
|
for provider_id in provider_ids:
|
||||||
name = provider_id
|
name = provider_id
|
||||||
|
|
|
@ -513,7 +513,7 @@ def sso(request):
|
||||||
For SOAP a session must be established previously through the login
|
For SOAP a session must be established previously through the login
|
||||||
page. No authentication through the SOAP request is supported.
|
page. No authentication through the SOAP request is supported.
|
||||||
"""
|
"""
|
||||||
if request.method == "GET":
|
if request.method == 'GET':
|
||||||
logger.debug('called by GET')
|
logger.debug('called by GET')
|
||||||
consent_answer = request.GET.get('consent_answer', '')
|
consent_answer = request.GET.get('consent_answer', '')
|
||||||
if consent_answer:
|
if consent_answer:
|
||||||
|
@ -544,14 +544,14 @@ def sso(request):
|
||||||
extra={'request': request},
|
extra={'request': request},
|
||||||
)
|
)
|
||||||
return HttpResponseBadRequest(
|
return HttpResponseBadRequest(
|
||||||
_("SAMLv2 Single Sign On: invalid message for WebSSO profile with HTTP-Redirect binding"),
|
_('SAMLv2 Single Sign On: invalid message for WebSSO profile with HTTP-Redirect binding'),
|
||||||
content_type='text/plain',
|
content_type='text/plain',
|
||||||
)
|
)
|
||||||
except lasso.ProfileInvalidProtocolprofileError:
|
except lasso.ProfileInvalidProtocolprofileError:
|
||||||
log_info_authn_request_details(login)
|
log_info_authn_request_details(login)
|
||||||
message = _(
|
message = _(
|
||||||
"SAMLv2 Single Sign On: the request cannot be answered because no valid protocol binding"
|
'SAMLv2 Single Sign On: the request cannot be answered because no valid protocol binding'
|
||||||
" could be found"
|
' could be found'
|
||||||
)
|
)
|
||||||
logger.warning('the request cannot be answered because no valid protocol binding could be found')
|
logger.warning('the request cannot be answered because no valid protocol binding could be found')
|
||||||
return HttpResponseBadRequest(message, content_type='text/plain')
|
return HttpResponseBadRequest(message, content_type='text/plain')
|
||||||
|
@ -689,7 +689,7 @@ def need_consent_for_federation(request, login, nid_format):
|
||||||
def continue_sso(request):
|
def continue_sso(request):
|
||||||
consent_answer = None
|
consent_answer = None
|
||||||
consent_attribute_answer = None
|
consent_attribute_answer = None
|
||||||
if request.method == "GET":
|
if request.method == 'GET':
|
||||||
logger.debug('called by GET')
|
logger.debug('called by GET')
|
||||||
consent_answer = request.GET.get('consent_answer', '')
|
consent_answer = request.GET.get('consent_answer', '')
|
||||||
if consent_answer:
|
if consent_answer:
|
||||||
|
|
|
@ -49,7 +49,7 @@ class Command(BaseCommand):
|
||||||
}
|
}
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
parser.add_argument("--fake", action='store_true', help='do nothing', default=False)
|
parser.add_argument('--fake', action='store_true', help='do nothing', default=False)
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
self.fake = options['fake']
|
self.fake = options['fake']
|
||||||
|
|
|
@ -68,7 +68,7 @@ class Command(BaseCommand):
|
||||||
def handle(self, filename, **options):
|
def handle(self, filename, **options):
|
||||||
translation.activate(settings.LANGUAGE_CODE)
|
translation.activate(settings.LANGUAGE_CODE)
|
||||||
dry_run = options['dry_run']
|
dry_run = options['dry_run']
|
||||||
msg = "Dry run\n" if dry_run else "Real run\n"
|
msg = 'Dry run\n' if dry_run else 'Real run\n'
|
||||||
c_kwargs = create_context_args(options)
|
c_kwargs = create_context_args(options)
|
||||||
try:
|
try:
|
||||||
with open(filename) as f:
|
with open(filename) as f:
|
||||||
|
@ -80,5 +80,5 @@ class Command(BaseCommand):
|
||||||
except DryRunException:
|
except DryRunException:
|
||||||
pass
|
pass
|
||||||
sys.stdout.write(result.to_str())
|
sys.stdout.write(result.to_str())
|
||||||
sys.stdout.write("Success\n")
|
sys.stdout.write('Success\n')
|
||||||
translation.deactivate()
|
translation.deactivate()
|
||||||
|
|
|
@ -111,7 +111,7 @@ class Command(BaseCommand):
|
||||||
'''Load LDAP ldif file'''
|
'''Load LDAP ldif file'''
|
||||||
|
|
||||||
can_import_django_settings = True
|
can_import_django_settings = True
|
||||||
requires_system_checks = "__all__"
|
requires_system_checks = '__all__'
|
||||||
help = 'Load/update LDIF files as users'
|
help = 'Load/update LDIF files as users'
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
|
|
|
@ -41,10 +41,10 @@ class Command(BaseCommand):
|
||||||
help='Specifies the database to use. Default is "default".',
|
help='Specifies the database to use. Default is "default".',
|
||||||
)
|
)
|
||||||
|
|
||||||
def _get_pass(self, prompt="Password: "):
|
def _get_pass(self, prompt='Password: '):
|
||||||
p = getpass.getpass(prompt=prompt)
|
p = getpass.getpass(prompt=prompt)
|
||||||
if not p:
|
if not p:
|
||||||
raise CommandError("aborted")
|
raise CommandError('aborted')
|
||||||
return p
|
return p
|
||||||
|
|
||||||
def handle(self, *args, **options):
|
def handle(self, *args, **options):
|
||||||
|
|
|
@ -145,8 +145,8 @@ class ChooseUserAuthorizationsForm(CssClass, forms.Form):
|
||||||
|
|
||||||
|
|
||||||
class UserEditForm(LimitQuerysetFormMixin, CssClass, BaseUserForm):
|
class UserEditForm(LimitQuerysetFormMixin, CssClass, BaseUserForm):
|
||||||
css_class = "user-form"
|
css_class = 'user-form'
|
||||||
form_id = "id_user_edit_form"
|
form_id = 'id_user_edit_form'
|
||||||
|
|
||||||
def __init__(self, *args, **kwargs):
|
def __init__(self, *args, **kwargs):
|
||||||
request = kwargs.get('request')
|
request = kwargs.get('request')
|
||||||
|
@ -193,8 +193,8 @@ class UserChangePasswordForm(CssClass, forms.ModelForm):
|
||||||
require_password = True
|
require_password = True
|
||||||
|
|
||||||
def clean_password2(self):
|
def clean_password2(self):
|
||||||
password1 = self.cleaned_data.get("password1")
|
password1 = self.cleaned_data.get('password1')
|
||||||
password2 = self.cleaned_data.get("password2")
|
password2 = self.cleaned_data.get('password2')
|
||||||
if password1 and password2 and password1 != password2:
|
if password1 and password2 and password1 != password2:
|
||||||
raise forms.ValidationError(
|
raise forms.ValidationError(
|
||||||
self.error_messages['password_mismatch'],
|
self.error_messages['password_mismatch'],
|
||||||
|
@ -232,7 +232,7 @@ class UserChangePasswordForm(CssClass, forms.ModelForm):
|
||||||
new_password = generate_password()
|
new_password = generate_password()
|
||||||
self.cleaned_data['send_mail'] = True
|
self.cleaned_data['send_mail'] = True
|
||||||
elif self.cleaned_data.get('password1'):
|
elif self.cleaned_data.get('password1'):
|
||||||
new_password = self.cleaned_data["password1"]
|
new_password = self.cleaned_data['password1']
|
||||||
|
|
||||||
if new_password:
|
if new_password:
|
||||||
user.set_password(new_password)
|
user.set_password(new_password)
|
||||||
|
@ -252,8 +252,8 @@ class UserChangePasswordForm(CssClass, forms.ModelForm):
|
||||||
return user
|
return user
|
||||||
|
|
||||||
generate_password = forms.BooleanField(initial=False, label=_('Generate new password'), required=False)
|
generate_password = forms.BooleanField(initial=False, label=_('Generate new password'), required=False)
|
||||||
password1 = NewPasswordField(label=_("Password"), required=False)
|
password1 = NewPasswordField(label=_('Password'), required=False)
|
||||||
password2 = CheckPasswordField(label=_("Confirmation"), required=False)
|
password2 = CheckPasswordField(label=_('Confirmation'), required=False)
|
||||||
send_mail = forms.BooleanField(initial=False, label=_('Send informations to user'), required=False)
|
send_mail = forms.BooleanField(initial=False, label=_('Send informations to user'), required=False)
|
||||||
|
|
||||||
def __init__(self, **kwargs):
|
def __init__(self, **kwargs):
|
||||||
|
@ -266,8 +266,8 @@ class UserChangePasswordForm(CssClass, forms.ModelForm):
|
||||||
|
|
||||||
|
|
||||||
class UserAddForm(UserChangePasswordForm, UserEditForm):
|
class UserAddForm(UserChangePasswordForm, UserEditForm):
|
||||||
css_class = "user-form"
|
css_class = 'user-form'
|
||||||
form_id = "id_user_add_form"
|
form_id = 'id_user_add_form'
|
||||||
require_password = False
|
require_password = False
|
||||||
|
|
||||||
notification_template_prefix = 'authentic2/manager/new-account-notification'
|
notification_template_prefix = 'authentic2/manager/new-account-notification'
|
||||||
|
@ -417,7 +417,7 @@ class OUSearchForm(FormWithRequest):
|
||||||
# get possible OUs from this list
|
# get possible OUs from this list
|
||||||
related_query_name = self.queryset.model._meta.get_field('ou').related_query_name()
|
related_query_name = self.queryset.model._meta.get_field('ou').related_query_name()
|
||||||
objects_ou_qs = OrganizationalUnit.objects.filter(
|
objects_ou_qs = OrganizationalUnit.objects.filter(
|
||||||
**{"%s__in" % related_query_name: self.queryset}
|
**{'%s__in' % related_query_name: self.queryset}
|
||||||
).distinct()
|
).distinct()
|
||||||
# to combine queryset with distinct, each queryset must have the distinct flag
|
# to combine queryset with distinct, each queryset must have the distinct flag
|
||||||
self.ou_qs = self.ou_qs.distinct() | objects_ou_qs
|
self.ou_qs = self.ou_qs.distinct() | objects_ou_qs
|
||||||
|
|
|
@ -816,8 +816,8 @@ class UserOrRoleSelect2View(DetailView):
|
||||||
|
|
||||||
return JsonResponse(
|
return JsonResponse(
|
||||||
{
|
{
|
||||||
"results": [self.get_choice(obj) for obj in list(role_page) + list(user_page)],
|
'results': [self.get_choice(obj) for obj in list(role_page) + list(user_page)],
|
||||||
"more": has_next,
|
'more': has_next,
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ TABLES_MAJOR_VERSION = int(tables.__version__.split('.', maxsplit=1)[0])
|
||||||
|
|
||||||
class Table(tables.Table):
|
class Table(tables.Table):
|
||||||
class Meta:
|
class Meta:
|
||||||
row_attrs = {"data-pk": lambda record: record.pk}
|
row_attrs = {'data-pk': lambda record: record.pk}
|
||||||
|
|
||||||
|
|
||||||
class PermissionLinkColumn(tables.LinkColumn):
|
class PermissionLinkColumn(tables.LinkColumn):
|
||||||
|
@ -98,12 +98,12 @@ class UserTable(Table):
|
||||||
args=[A('pk')],
|
args=[A('pk')],
|
||||||
order_by=('last_name', 'first_name', 'email', 'username'),
|
order_by=('last_name', 'first_name', 'email', 'username'),
|
||||||
text=lambda record: record.get_full_name(),
|
text=lambda record: record.get_full_name(),
|
||||||
attrs={"td": {"class": "link"}},
|
attrs={'td': {'class': 'link'}},
|
||||||
)
|
)
|
||||||
username = tables.Column(
|
username = tables.Column(
|
||||||
attrs={
|
attrs={
|
||||||
"td": {"class": "username"},
|
'td': {'class': 'username'},
|
||||||
"th": {"class": "username orderable"},
|
'th': {'class': 'username orderable'},
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
email = VerifiableEmailColumn()
|
email = VerifiableEmailColumn()
|
||||||
|
@ -119,18 +119,18 @@ class UserTable(Table):
|
||||||
|
|
||||||
class RoleMembersTable(UserTable):
|
class RoleMembersTable(UserTable):
|
||||||
direct = tables.BooleanColumn(
|
direct = tables.BooleanColumn(
|
||||||
verbose_name=_('Direct member'), orderable=False, attrs={"td": {"class": "direct"}}
|
verbose_name=_('Direct member'), orderable=False, attrs={'td': {'class': 'direct'}}
|
||||||
)
|
)
|
||||||
via = tables.TemplateColumn(
|
via = tables.TemplateColumn(
|
||||||
'{% for role in record.via %}<a href="{% url "a2-manager-role-members" pk=role.pk %}">{{ role'
|
'{% for role in record.via %}<a href="{% url "a2-manager-role-members" pk=role.pk %}">{{ role'
|
||||||
' }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}',
|
' }}</a>{% if not forloop.last %}, {% endif %}{% endfor %}',
|
||||||
verbose_name=_('Inherited from'),
|
verbose_name=_('Inherited from'),
|
||||||
orderable=False,
|
orderable=False,
|
||||||
attrs={"td": {"class": "via"}},
|
attrs={'td': {'class': 'via'}},
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(UserTable.Meta):
|
class Meta(UserTable.Meta):
|
||||||
row_attrs = {"data-pk": lambda record: 'user-%s' % record.pk}
|
row_attrs = {'data-pk': lambda record: 'user-%s' % record.pk}
|
||||||
|
|
||||||
|
|
||||||
class UserOrRoleColumn(UserLinkColumn):
|
class UserOrRoleColumn(UserLinkColumn):
|
||||||
|
@ -146,13 +146,13 @@ class MixedUserRoleTable(Table):
|
||||||
verbose_name=_('Members'),
|
verbose_name=_('Members'),
|
||||||
text=str,
|
text=str,
|
||||||
orderable=False,
|
orderable=False,
|
||||||
attrs={"td": {"class": "name"}},
|
attrs={'td': {'class': 'name'}},
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(Table.Meta):
|
class Meta(Table.Meta):
|
||||||
attrs = {'class': 'main clickable-rows', 'id': 'user-table'}
|
attrs = {'class': 'main clickable-rows', 'id': 'user-table'}
|
||||||
row_attrs = {
|
row_attrs = {
|
||||||
"data-pk": lambda record: '%s-%s' % ('user' if isinstance(record, User) else 'role', record.pk)
|
'data-pk': lambda record: '%s-%s' % ('user' if isinstance(record, User) else 'role', record.pk)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -162,12 +162,12 @@ class RoleTable(Table):
|
||||||
kwargs={'pk': A('pk')},
|
kwargs={'pk': A('pk')},
|
||||||
accessor='name',
|
accessor='name',
|
||||||
verbose_name=_('label'),
|
verbose_name=_('label'),
|
||||||
attrs={"td": {"class": "name"}},
|
attrs={'td': {'class': 'name'}},
|
||||||
)
|
)
|
||||||
ou = tables.Column()
|
ou = tables.Column()
|
||||||
slug = tables.Column(attrs={"td": {"class": "slug"}})
|
slug = tables.Column(attrs={'td': {'class': 'slug'}})
|
||||||
member_count = tables.Column(
|
member_count = tables.Column(
|
||||||
verbose_name=_('Direct member count'), orderable=False, attrs={"td": {"class": "member_count"}}
|
verbose_name=_('Direct member count'), orderable=False, attrs={'td': {'class': 'member_count'}}
|
||||||
)
|
)
|
||||||
|
|
||||||
def render_name(self, record, bound_column):
|
def render_name(self, record, bound_column):
|
||||||
|
@ -192,10 +192,10 @@ class OUTable(Table):
|
||||||
kwargs={'pk': A('pk')},
|
kwargs={'pk': A('pk')},
|
||||||
accessor='name',
|
accessor='name',
|
||||||
verbose_name=_('label'),
|
verbose_name=_('label'),
|
||||||
attrs={"td": {"class": "name"}},
|
attrs={'td': {'class': 'name'}},
|
||||||
)
|
)
|
||||||
slug = tables.Column(attrs={"td": {"class": "slug"}})
|
slug = tables.Column(attrs={'td': {'class': 'slug'}})
|
||||||
default = tables.BooleanColumn(attrs={"td": {"class": "default"}})
|
default = tables.BooleanColumn(attrs={'td': {'class': 'default'}})
|
||||||
|
|
||||||
class Meta(Table.Meta):
|
class Meta(Table.Meta):
|
||||||
model = OrganizationalUnit
|
model = OrganizationalUnit
|
||||||
|
@ -210,13 +210,13 @@ class OuUserRolesTable(Table):
|
||||||
kwargs={'pk': A('pk')},
|
kwargs={'pk': A('pk')},
|
||||||
accessor='name',
|
accessor='name',
|
||||||
verbose_name=_('label'),
|
verbose_name=_('label'),
|
||||||
attrs={"td": {"class": "name"}},
|
attrs={'td': {'class': 'name'}},
|
||||||
)
|
)
|
||||||
via = tables.TemplateColumn(
|
via = tables.TemplateColumn(
|
||||||
'''{% for rel in record.via %}{{ rel.child }} {% if not forloop.last %}, {% endif %}{% endfor %}''',
|
'''{% for rel in record.via %}{{ rel.child }} {% if not forloop.last %}, {% endif %}{% endfor %}''',
|
||||||
verbose_name=_('Inherited from'),
|
verbose_name=_('Inherited from'),
|
||||||
orderable=False,
|
orderable=False,
|
||||||
attrs={"td": {"class": "via"}},
|
attrs={'td': {'class': 'via'}},
|
||||||
)
|
)
|
||||||
member = tables.TemplateColumn(
|
member = tables.TemplateColumn(
|
||||||
'{%% load i18n %%}<input class="role-member{%% if not record.member and record.via %%}'
|
'{%% load i18n %%}<input class="role-member{%% if not record.member and record.via %%}'
|
||||||
|
@ -229,7 +229,7 @@ class OuUserRolesTable(Table):
|
||||||
),
|
),
|
||||||
verbose_name=_('Member'),
|
verbose_name=_('Member'),
|
||||||
order_by=('member', 'via', 'name'),
|
order_by=('member', 'via', 'name'),
|
||||||
attrs={"td": {"class": "member"}},
|
attrs={'td': {'class': 'member'}},
|
||||||
)
|
)
|
||||||
|
|
||||||
def render_name(self, record, bound_column):
|
def render_name(self, record, bound_column):
|
||||||
|
@ -255,7 +255,7 @@ class UserRolesTable(Table):
|
||||||
kwargs={'pk': A('pk')},
|
kwargs={'pk': A('pk')},
|
||||||
accessor='name',
|
accessor='name',
|
||||||
verbose_name=_('label'),
|
verbose_name=_('label'),
|
||||||
attrs={"td": {"class": "name"}},
|
attrs={'td': {'class': 'name'}},
|
||||||
)
|
)
|
||||||
ou = tables.Column()
|
ou = tables.Column()
|
||||||
via = tables.TemplateColumn(
|
via = tables.TemplateColumn(
|
||||||
|
@ -263,7 +263,7 @@ class UserRolesTable(Table):
|
||||||
'{{ rel.child }} {% if not forloop.last %}, {% endif %}{% endfor %}{% endif %}',
|
'{{ rel.child }} {% if not forloop.last %}, {% endif %}{% endfor %}{% endif %}',
|
||||||
verbose_name=_('Inherited from'),
|
verbose_name=_('Inherited from'),
|
||||||
orderable=False,
|
orderable=False,
|
||||||
attrs={"td": {"class": "via"}},
|
attrs={'td': {'class': 'via'}},
|
||||||
)
|
)
|
||||||
|
|
||||||
def render_name(self, record, bound_column):
|
def render_name(self, record, bound_column):
|
||||||
|
@ -331,19 +331,19 @@ class InheritanceRolesTable(Table):
|
||||||
kwargs={'pk': A('pk')},
|
kwargs={'pk': A('pk')},
|
||||||
accessor='name',
|
accessor='name',
|
||||||
verbose_name=_('label'),
|
verbose_name=_('label'),
|
||||||
attrs={"td": {"class": "name"}},
|
attrs={'td': {'class': 'name'}},
|
||||||
)
|
)
|
||||||
via = tables.TemplateColumn(
|
via = tables.TemplateColumn(
|
||||||
'''{% for rel in record.via %}{{ rel.name }}{% if not forloop.last %}, {% endif %}{% endfor %}''',
|
'''{% for rel in record.via %}{{ rel.name }}{% if not forloop.last %}, {% endif %}{% endfor %}''',
|
||||||
verbose_name=_('Inherited from'),
|
verbose_name=_('Inherited from'),
|
||||||
orderable=False,
|
orderable=False,
|
||||||
attrs={"td": {"class": "via"}},
|
attrs={'td': {'class': 'via'}},
|
||||||
)
|
)
|
||||||
member = tables.TemplateColumn(
|
member = tables.TemplateColumn(
|
||||||
'<input class="role-member{% if record.indeterminate %} indeterminate{% endif %}" name="role-{{ record.pk }}" '
|
'<input class="role-member{% if record.indeterminate %} indeterminate{% endif %}" name="role-{{ record.pk }}" '
|
||||||
'type="checkbox" {% if record.checked %}checked{% endif %}/>',
|
'type="checkbox" {% if record.checked %}checked{% endif %}/>',
|
||||||
verbose_name='',
|
verbose_name='',
|
||||||
attrs={"td": {"class": "member"}},
|
attrs={'td': {'class': 'member'}},
|
||||||
)
|
)
|
||||||
|
|
||||||
class Meta(Table.Meta):
|
class Meta(Table.Meta):
|
||||||
|
|
|
@ -51,9 +51,9 @@ def get_user_dataset(qs):
|
||||||
return ''
|
return ''
|
||||||
if hasattr(rec, 'strftime'):
|
if hasattr(rec, 'strftime'):
|
||||||
if isinstance(rec, datetime.datetime):
|
if isinstance(rec, datetime.datetime):
|
||||||
_format = "%Y-%m-%d %H:%M:%S"
|
_format = '%Y-%m-%d %H:%M:%S'
|
||||||
else:
|
else:
|
||||||
_format = "%Y-%m-%d"
|
_format = '%Y-%m-%d'
|
||||||
return rec.strftime(_format)
|
return rec.strftime(_format)
|
||||||
return rec
|
return rec
|
||||||
|
|
||||||
|
|
|
@ -129,7 +129,7 @@ class PermissionMixin:
|
||||||
return self.permission_model.objects.get(pk=self.kwargs[self.permission_pk_url_kwarg])
|
return self.permission_model.objects.get(pk=self.kwargs[self.permission_pk_url_kwarg])
|
||||||
except self.permission_model.DoesNotExist:
|
except self.permission_model.DoesNotExist:
|
||||||
raise Http404(
|
raise Http404(
|
||||||
gettext("No %(verbose_name)s found matching the query")
|
gettext('No %(verbose_name)s found matching the query')
|
||||||
% {'verbose_name': self.permission_model._meta.verbose_name}
|
% {'verbose_name': self.permission_model._meta.verbose_name}
|
||||||
)
|
)
|
||||||
elif hasattr(self, 'get_object') and (
|
elif hasattr(self, 'get_object') and (
|
||||||
|
@ -751,7 +751,7 @@ homepage = HomepageView.as_view()
|
||||||
|
|
||||||
class TechnicalInformationView(TitleMixin, MediaMixin, TemplateView):
|
class TechnicalInformationView(TitleMixin, MediaMixin, TemplateView):
|
||||||
template_name = 'authentic2/manager/tech_info.html'
|
template_name = 'authentic2/manager/tech_info.html'
|
||||||
title = _("Technical information")
|
title = _('Technical information')
|
||||||
|
|
||||||
def get(self, request, *args, **kwargs):
|
def get(self, request, *args, **kwargs):
|
||||||
if not request.user.is_superuser:
|
if not request.user.is_superuser:
|
||||||
|
|
|
@ -208,7 +208,7 @@ class XForwardedForMiddleware(MiddlewareMixin):
|
||||||
|
|
||||||
def process_request(self, request):
|
def process_request(self, request):
|
||||||
if 'x-forwarded-for' in request.headers:
|
if 'x-forwarded-for' in request.headers:
|
||||||
request.META['REMOTE_ADDR'] = request.headers['X-Forwarded-For'].split(",")[0].strip()
|
request.META['REMOTE_ADDR'] = request.headers['X-Forwarded-For'].split(',')[0].strip()
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -157,7 +157,7 @@ class Migration(migrations.Migration):
|
||||||
default=300,
|
default=300,
|
||||||
help_text=(
|
help_text=(
|
||||||
"if iframe logout is used, it's the time between the onload event for this iframe"
|
"if iframe logout is used, it's the time between the onload event for this iframe"
|
||||||
" and the moment we consider its loading to be really finished"
|
' and the moment we consider its loading to be really finished'
|
||||||
),
|
),
|
||||||
verbose_name='iframe logout timeout (ms)',
|
verbose_name='iframe logout timeout (ms)',
|
||||||
),
|
),
|
||||||
|
|
|
@ -11,18 +11,18 @@ class Migration(migrations.Migration):
|
||||||
operations = [
|
operations = [
|
||||||
migrations.RunSQL(
|
migrations.RunSQL(
|
||||||
sql=[
|
sql=[
|
||||||
"CREATE EXTENSION IF NOT EXISTS unaccent SCHEMA public",
|
'CREATE EXTENSION IF NOT EXISTS unaccent SCHEMA public',
|
||||||
"CREATE EXTENSION IF NOT EXISTS pg_trgm SCHEMA public",
|
'CREATE EXTENSION IF NOT EXISTS pg_trgm SCHEMA public',
|
||||||
"CREATE OR REPLACE FUNCTION public.immutable_unaccent(text) RETURNS varchar AS $$ SELECT"
|
'CREATE OR REPLACE FUNCTION public.immutable_unaccent(text) RETURNS varchar AS $$ SELECT'
|
||||||
" public.unaccent('public.unaccent',$1::text); $$ LANGUAGE 'sql' IMMUTABLE",
|
" public.unaccent('public.unaccent',$1::text); $$ LANGUAGE 'sql' IMMUTABLE",
|
||||||
"CREATE INDEX custom_user_name_gist_idx ON custom_user_user USING gist"
|
'CREATE INDEX custom_user_name_gist_idx ON custom_user_user USING gist'
|
||||||
" (LOWER(public.immutable_unaccent(first_name || ' ' || last_name)) public.gist_trgm_ops)",
|
" (LOWER(public.immutable_unaccent(first_name || ' ' || last_name)) public.gist_trgm_ops)",
|
||||||
],
|
],
|
||||||
reverse_sql=[
|
reverse_sql=[
|
||||||
"DROP INDEX IF EXISTS custom_user_name_gist_idx",
|
'DROP INDEX IF EXISTS custom_user_name_gist_idx',
|
||||||
"DROP FUNCTION IF EXISTS public.immutable_unaccent(text)",
|
'DROP FUNCTION IF EXISTS public.immutable_unaccent(text)',
|
||||||
"DROP EXTENSION IF EXISTS pg_trgm",
|
'DROP EXTENSION IF EXISTS pg_trgm',
|
||||||
"DROP EXTENSION IF EXISTS unaccent",
|
'DROP EXTENSION IF EXISTS unaccent',
|
||||||
],
|
],
|
||||||
),
|
),
|
||||||
]
|
]
|
||||||
|
|
|
@ -11,7 +11,7 @@ def clean_admin_tools_tables(apps, schema_editor):
|
||||||
try:
|
try:
|
||||||
with schema_editor.connection.cursor() as cursor:
|
with schema_editor.connection.cursor() as cursor:
|
||||||
cursor.execute(
|
cursor.execute(
|
||||||
"SELECT table_name FROM information_schema.tables WHERE table_schema = current_schema() AND"
|
'SELECT table_name FROM information_schema.tables WHERE table_schema = current_schema() AND'
|
||||||
" table_name LIKE 'admin_tools%'"
|
" table_name LIKE 'admin_tools%'"
|
||||||
)
|
)
|
||||||
rows = cursor.fetchall()
|
rows = cursor.fetchall()
|
||||||
|
|
|
@ -86,7 +86,7 @@ class CreatePartialIndexes(Operation):
|
||||||
schema_editor.execute('DROP INDEX IF EXISTS "%s_%s"' % (self.index_name, i))
|
schema_editor.execute('DROP INDEX IF EXISTS "%s_%s"' % (self.index_name, i))
|
||||||
|
|
||||||
def describe(self):
|
def describe(self):
|
||||||
return "Create partial indexes"
|
return 'Create partial indexes'
|
||||||
|
|
||||||
|
|
||||||
class DropPartialIndexes(CreatePartialIndexes):
|
class DropPartialIndexes(CreatePartialIndexes):
|
||||||
|
@ -102,4 +102,4 @@ class DropPartialIndexes(CreatePartialIndexes):
|
||||||
super().database_forwards(app_label, schema_editor, from_state, to_state)
|
super().database_forwards(app_label, schema_editor, from_state, to_state)
|
||||||
|
|
||||||
def describe(self):
|
def describe(self):
|
||||||
return "Drop partial indexes"
|
return 'Drop partial indexes'
|
||||||
|
|
|
@ -42,15 +42,15 @@ from authentic2.utils.cache import RequestCache
|
||||||
|
|
||||||
from .. import nonce
|
from .. import nonce
|
||||||
|
|
||||||
AUTHENTIC_STATUS_CODE_NS = "http://authentic.entrouvert.org/status_code/"
|
AUTHENTIC_STATUS_CODE_NS = 'http://authentic.entrouvert.org/status_code/'
|
||||||
AUTHENTIC_SAME_ID_SENTINEL = 'urn:authentic.entrouvert.org:same-as-provider-entity-id'
|
AUTHENTIC_SAME_ID_SENTINEL = 'urn:authentic.entrouvert.org:same-as-provider-entity-id'
|
||||||
AUTHENTIC_STATUS_CODE_UNKNOWN_PROVIDER = AUTHENTIC_STATUS_CODE_NS + "UnknownProvider"
|
AUTHENTIC_STATUS_CODE_UNKNOWN_PROVIDER = AUTHENTIC_STATUS_CODE_NS + 'UnknownProvider'
|
||||||
AUTHENTIC_STATUS_CODE_MISSING_NAMEID = AUTHENTIC_STATUS_CODE_NS + "MissingNameID"
|
AUTHENTIC_STATUS_CODE_MISSING_NAMEID = AUTHENTIC_STATUS_CODE_NS + 'MissingNameID'
|
||||||
AUTHENTIC_STATUS_CODE_MISSING_SESSION_INDEX = AUTHENTIC_STATUS_CODE_NS + "MissingSessionIndex"
|
AUTHENTIC_STATUS_CODE_MISSING_SESSION_INDEX = AUTHENTIC_STATUS_CODE_NS + 'MissingSessionIndex'
|
||||||
AUTHENTIC_STATUS_CODE_UNKNOWN_SESSION = AUTHENTIC_STATUS_CODE_NS + "UnknownSession"
|
AUTHENTIC_STATUS_CODE_UNKNOWN_SESSION = AUTHENTIC_STATUS_CODE_NS + 'UnknownSession'
|
||||||
AUTHENTIC_STATUS_CODE_MISSING_DESTINATION = AUTHENTIC_STATUS_CODE_NS + "MissingDestination"
|
AUTHENTIC_STATUS_CODE_MISSING_DESTINATION = AUTHENTIC_STATUS_CODE_NS + 'MissingDestination'
|
||||||
AUTHENTIC_STATUS_CODE_INTERNAL_SERVER_ERROR = AUTHENTIC_STATUS_CODE_NS + "InternalServerError"
|
AUTHENTIC_STATUS_CODE_INTERNAL_SERVER_ERROR = AUTHENTIC_STATUS_CODE_NS + 'InternalServerError'
|
||||||
AUTHENTIC_STATUS_CODE_UNAUTHORIZED = AUTHENTIC_STATUS_CODE_NS + "Unauthorized"
|
AUTHENTIC_STATUS_CODE_UNAUTHORIZED = AUTHENTIC_STATUS_CODE_NS + 'Unauthorized'
|
||||||
|
|
||||||
logger = logging.getLogger(__name__)
|
logger = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
@ -542,7 +542,7 @@ def soap_fault(request, faultcode='soap:Client', faultstring=None):
|
||||||
</soap:Envelope>'''
|
</soap:Envelope>'''
|
||||||
% locals()
|
% locals()
|
||||||
)
|
)
|
||||||
return HttpResponse(content, content_type="text/xml")
|
return HttpResponse(content, content_type='text/xml')
|
||||||
|
|
||||||
|
|
||||||
@RequestCache
|
@RequestCache
|
||||||
|
|
|
@ -128,7 +128,7 @@ class MultiSelectFormField(forms.MultipleChoiceField):
|
||||||
|
|
||||||
class MultiSelectField(models.Field):
|
class MultiSelectField(models.Field):
|
||||||
def get_internal_type(self):
|
def get_internal_type(self):
|
||||||
return "CharField"
|
return 'CharField'
|
||||||
|
|
||||||
def get_choices_default(self):
|
def get_choices_default(self):
|
||||||
return self.get_choices(include_blank=False)
|
return self.get_choices(include_blank=False)
|
||||||
|
@ -153,7 +153,7 @@ class MultiSelectField(models.Field):
|
||||||
if isinstance(value, str):
|
if isinstance(value, str):
|
||||||
return value
|
return value
|
||||||
elif isinstance(value, list):
|
elif isinstance(value, list):
|
||||||
return ",".join(value)
|
return ','.join(value)
|
||||||
|
|
||||||
def validate(self, value, model_instance):
|
def validate(self, value, model_instance):
|
||||||
out = set()
|
out = set()
|
||||||
|
@ -170,7 +170,7 @@ class MultiSelectField(models.Field):
|
||||||
return value
|
return value
|
||||||
if not value:
|
if not value:
|
||||||
return []
|
return []
|
||||||
return value.split(",")
|
return value.split(',')
|
||||||
|
|
||||||
def from_db_value(self, value, expression, connection):
|
def from_db_value(self, value, expression, connection):
|
||||||
return self.to_python(value)
|
return self.to_python(value)
|
||||||
|
@ -181,6 +181,6 @@ class MultiSelectField(models.Field):
|
||||||
|
|
||||||
def func(self, fieldname=name, choicedict=None):
|
def func(self, fieldname=name, choicedict=None):
|
||||||
choicedict = choicedict or dict(self.choices)
|
choicedict = choicedict or dict(self.choices)
|
||||||
return ",".join([choicedict.get(value, value) for value in getattr(self, fieldname)])
|
return ','.join([choicedict.get(value, value) for value in getattr(self, fieldname)])
|
||||||
|
|
||||||
setattr(cls, 'get_%s_display' % self.name, func)
|
setattr(cls, 'get_%s_display' % self.name, func)
|
||||||
|
|
|
@ -33,7 +33,7 @@ from .models import LibertyProvider, LibertyServiceProvider
|
||||||
class AddLibertyProviderFromUrlForm(forms.Form):
|
class AddLibertyProviderFromUrlForm(forms.Form):
|
||||||
name = forms.CharField(max_length=140, label=_('Name'))
|
name = forms.CharField(max_length=140, label=_('Name'))
|
||||||
slug = forms.SlugField(
|
slug = forms.SlugField(
|
||||||
max_length=140, label=_('Shortcut'), help_text=_("Internal nickname for the service provider")
|
max_length=140, label=_('Shortcut'), help_text=_('Internal nickname for the service provider')
|
||||||
)
|
)
|
||||||
url = forms.URLField(label=_("Metadata's URL"))
|
url = forms.URLField(label=_("Metadata's URL"))
|
||||||
ou = forms.ModelChoiceField(
|
ou = forms.ModelChoiceField(
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -255,7 +255,7 @@ class Command(BaseCommand):
|
||||||
|
|
||||||
can_import_django_settings = True
|
can_import_django_settings = True
|
||||||
output_transaction = True
|
output_transaction = True
|
||||||
requires_system_checks = "__all__"
|
requires_system_checks = '__all__'
|
||||||
|
|
||||||
help = 'Load the specified SAMLv2 metadata file'
|
help = 'Load the specified SAMLv2 metadata file'
|
||||||
|
|
||||||
|
|
|
@ -692,7 +692,7 @@ class Migration(migrations.Migration):
|
||||||
default=300,
|
default=300,
|
||||||
help_text=(
|
help_text=(
|
||||||
"if iframe logout is used, it's the time between the onload event for this iframe"
|
"if iframe logout is used, it's the time between the onload event for this iframe"
|
||||||
" and the moment we consider its loading to be really finished"
|
' and the moment we consider its loading to be really finished'
|
||||||
),
|
),
|
||||||
verbose_name='iframe logout timeout',
|
verbose_name='iframe logout timeout',
|
||||||
),
|
),
|
||||||
|
|
|
@ -127,35 +127,35 @@ NAME_ID_FORMATS = collections.OrderedDict(
|
||||||
(
|
(
|
||||||
'transient',
|
'transient',
|
||||||
{
|
{
|
||||||
'caption': _("Transient"),
|
'caption': _('Transient'),
|
||||||
'samlv2': lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT,
|
'samlv2': lasso.SAML2_NAME_IDENTIFIER_FORMAT_TRANSIENT,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'email',
|
'email',
|
||||||
{
|
{
|
||||||
'caption': _("Email"),
|
'caption': _('Email'),
|
||||||
'samlv2': lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL,
|
'samlv2': lasso.SAML2_NAME_IDENTIFIER_FORMAT_EMAIL,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'username',
|
'username',
|
||||||
{
|
{
|
||||||
'caption': _("Username (use with Google Apps)"),
|
'caption': _('Username (use with Google Apps)'),
|
||||||
'samlv2': lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED,
|
'samlv2': lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'uuid',
|
'uuid',
|
||||||
{
|
{
|
||||||
'caption': _("UUID"),
|
'caption': _('UUID'),
|
||||||
'samlv2': lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED,
|
'samlv2': lasso.SAML2_NAME_IDENTIFIER_FORMAT_UNSPECIFIED,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
'edupersontargetedid',
|
'edupersontargetedid',
|
||||||
{
|
{
|
||||||
'caption': _("Use eduPersonTargetedID attribute"),
|
'caption': _('Use eduPersonTargetedID attribute'),
|
||||||
'samlv2': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
|
'samlv2': lasso.SAML2_NAME_IDENTIFIER_FORMAT_PERSISTENT,
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
|
@ -222,16 +222,16 @@ class SPOptionsIdPPolicy(models.Model):
|
||||||
name = models.CharField(_('name'), max_length=80, unique=True)
|
name = models.CharField(_('name'), max_length=80, unique=True)
|
||||||
enabled = models.BooleanField(verbose_name=_('Enabled'), default=False, db_index=True)
|
enabled = models.BooleanField(verbose_name=_('Enabled'), default=False, db_index=True)
|
||||||
prefered_assertion_consumer_binding = models.CharField(
|
prefered_assertion_consumer_binding = models.CharField(
|
||||||
verbose_name=_("Prefered assertion consumer binding"),
|
verbose_name=_('Prefered assertion consumer binding'),
|
||||||
default='meta',
|
default='meta',
|
||||||
max_length=4,
|
max_length=4,
|
||||||
choices=ASSERTION_CONSUMER_PROFILES,
|
choices=ASSERTION_CONSUMER_PROFILES,
|
||||||
)
|
)
|
||||||
encrypt_nameid = models.BooleanField(verbose_name=_("Encrypt NameID"), default=False)
|
encrypt_nameid = models.BooleanField(verbose_name=_('Encrypt NameID'), default=False)
|
||||||
encrypt_assertion = models.BooleanField(verbose_name=_("Encrypt Assertion"), default=False)
|
encrypt_assertion = models.BooleanField(verbose_name=_('Encrypt Assertion'), default=False)
|
||||||
authn_request_signed = models.BooleanField(verbose_name=_("Authentication request signed"), default=False)
|
authn_request_signed = models.BooleanField(verbose_name=_('Authentication request signed'), default=False)
|
||||||
idp_initiated_sso = models.BooleanField(
|
idp_initiated_sso = models.BooleanField(
|
||||||
verbose_name=_("Allow IdP initiated SSO"), default=False, db_index=True
|
verbose_name=_('Allow IdP initiated SSO'), default=False, db_index=True
|
||||||
)
|
)
|
||||||
# XXX: format in the metadata file, should be suffixed with a star to mark
|
# XXX: format in the metadata file, should be suffixed with a star to mark
|
||||||
# them as special
|
# them as special
|
||||||
|
@ -239,7 +239,7 @@ class SPOptionsIdPPolicy(models.Model):
|
||||||
max_length=256, default=DEFAULT_NAME_ID_FORMAT, choices=NAME_ID_FORMATS_CHOICES
|
max_length=256, default=DEFAULT_NAME_ID_FORMAT, choices=NAME_ID_FORMATS_CHOICES
|
||||||
)
|
)
|
||||||
accepted_name_id_format = MultiSelectField(
|
accepted_name_id_format = MultiSelectField(
|
||||||
verbose_name=_("NameID formats accepted"),
|
verbose_name=_('NameID formats accepted'),
|
||||||
max_length=1024,
|
max_length=1024,
|
||||||
blank=True,
|
blank=True,
|
||||||
choices=NAME_ID_FORMATS_CHOICES,
|
choices=NAME_ID_FORMATS_CHOICES,
|
||||||
|
@ -251,9 +251,9 @@ class SPOptionsIdPPolicy(models.Model):
|
||||||
verbose_name=_('Ask user for consent when creating a federation'), default=False
|
verbose_name=_('Ask user for consent when creating a federation'), default=False
|
||||||
)
|
)
|
||||||
accept_slo = models.BooleanField(
|
accept_slo = models.BooleanField(
|
||||||
verbose_name=_("Accept to receive Single Logout requests"), default=True, db_index=True
|
verbose_name=_('Accept to receive Single Logout requests'), default=True, db_index=True
|
||||||
)
|
)
|
||||||
forward_slo = models.BooleanField(verbose_name=_("Forward Single Logout requests"), default=True)
|
forward_slo = models.BooleanField(verbose_name=_('Forward Single Logout requests'), default=True)
|
||||||
needs_iframe_logout = models.BooleanField(
|
needs_iframe_logout = models.BooleanField(
|
||||||
verbose_name=_('needs iframe logout'),
|
verbose_name=_('needs iframe logout'),
|
||||||
help_text=_(
|
help_text=_(
|
||||||
|
@ -271,7 +271,7 @@ class SPOptionsIdPPolicy(models.Model):
|
||||||
default=300,
|
default=300,
|
||||||
)
|
)
|
||||||
http_method_for_slo_request = models.IntegerField(
|
http_method_for_slo_request = models.IntegerField(
|
||||||
verbose_name=_("HTTP binding for the SLO requests"),
|
verbose_name=_('HTTP binding for the SLO requests'),
|
||||||
choices=HTTP_METHOD,
|
choices=HTTP_METHOD,
|
||||||
default=lasso.HTTP_METHOD_REDIRECT,
|
default=lasso.HTTP_METHOD_REDIRECT,
|
||||||
)
|
)
|
||||||
|
@ -473,7 +473,7 @@ class LibertyServiceProvider(models.Model):
|
||||||
)
|
)
|
||||||
sp_options_policy = models.ForeignKey(
|
sp_options_policy = models.ForeignKey(
|
||||||
SPOptionsIdPPolicy,
|
SPOptionsIdPPolicy,
|
||||||
related_name="sp_options_policy",
|
related_name='sp_options_policy',
|
||||||
verbose_name=_('service provider options policy'),
|
verbose_name=_('service provider options policy'),
|
||||||
blank=True,
|
blank=True,
|
||||||
null=True,
|
null=True,
|
||||||
|
@ -556,11 +556,11 @@ class LibertyFederation(models.Model):
|
||||||
|
|
||||||
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL)
|
user = models.ForeignKey(settings.AUTH_USER_MODEL, null=True, blank=True, on_delete=models.SET_NULL)
|
||||||
sp = models.ForeignKey('LibertyServiceProvider', null=True, blank=True, on_delete=models.CASCADE)
|
sp = models.ForeignKey('LibertyServiceProvider', null=True, blank=True, on_delete=models.CASCADE)
|
||||||
name_id_format = models.CharField(max_length=100, verbose_name="NameIDFormat", blank=True, null=True)
|
name_id_format = models.CharField(max_length=100, verbose_name='NameIDFormat', blank=True, null=True)
|
||||||
name_id_content = models.CharField(max_length=100, verbose_name="NameID")
|
name_id_content = models.CharField(max_length=100, verbose_name='NameID')
|
||||||
name_id_qualifier = models.CharField(max_length=256, verbose_name="NameQualifier", blank=True, null=True)
|
name_id_qualifier = models.CharField(max_length=256, verbose_name='NameQualifier', blank=True, null=True)
|
||||||
name_id_sp_name_qualifier = models.CharField(
|
name_id_sp_name_qualifier = models.CharField(
|
||||||
max_length=256, verbose_name="SPNameQualifier", blank=True, null=True
|
max_length=256, verbose_name='SPNameQualifier', blank=True, null=True
|
||||||
)
|
)
|
||||||
termination_notified = models.BooleanField(blank=True, default=False)
|
termination_notified = models.BooleanField(blank=True, default=False)
|
||||||
creation = models.DateTimeField(auto_now_add=True)
|
creation = models.DateTimeField(auto_now_add=True)
|
||||||
|
@ -599,8 +599,8 @@ class LibertyFederation(models.Model):
|
||||||
return not qs.exists()
|
return not qs.exists()
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("SAML federation")
|
verbose_name = _('SAML federation')
|
||||||
verbose_name_plural = _("SAML federations")
|
verbose_name_plural = _('SAML federations')
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return str(self.name_id_content)
|
return str(self.name_id_content)
|
||||||
|
@ -613,10 +613,10 @@ class LibertySession(models.Model):
|
||||||
session_index = models.CharField(max_length=80)
|
session_index = models.CharField(max_length=80)
|
||||||
provider_id = models.CharField(max_length=256)
|
provider_id = models.CharField(max_length=256)
|
||||||
federation = models.ForeignKey(LibertyFederation, blank=True, null=True, on_delete=models.CASCADE)
|
federation = models.ForeignKey(LibertyFederation, blank=True, null=True, on_delete=models.CASCADE)
|
||||||
name_id_qualifier = models.CharField(max_length=256, verbose_name=_("Qualifier"), null=True)
|
name_id_qualifier = models.CharField(max_length=256, verbose_name=_('Qualifier'), null=True)
|
||||||
name_id_format = models.CharField(max_length=100, verbose_name=_("NameIDFormat"), null=True)
|
name_id_format = models.CharField(max_length=100, verbose_name=_('NameIDFormat'), null=True)
|
||||||
name_id_content = models.CharField(max_length=100, verbose_name=_("NameID"))
|
name_id_content = models.CharField(max_length=100, verbose_name=_('NameID'))
|
||||||
name_id_sp_name_qualifier = models.CharField(max_length=256, verbose_name=_("SPNameQualifier"), null=True)
|
name_id_sp_name_qualifier = models.CharField(max_length=256, verbose_name=_('SPNameQualifier'), null=True)
|
||||||
creation = models.DateTimeField(auto_now_add=True)
|
creation = models.DateTimeField(auto_now_add=True)
|
||||||
|
|
||||||
objects = managers.LibertySessionManager()
|
objects = managers.LibertySessionManager()
|
||||||
|
@ -654,8 +654,8 @@ class LibertySession(models.Model):
|
||||||
return '<LibertySession %s>' % self.__dict__
|
return '<LibertySession %s>' % self.__dict__
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("SAML session")
|
verbose_name = _('SAML session')
|
||||||
verbose_name_plural = _("SAML sessions")
|
verbose_name_plural = _('SAML sessions')
|
||||||
indexes = [
|
indexes = [
|
||||||
models.Index(fields=['provider_id', 'django_session_key']),
|
models.Index(fields=['provider_id', 'django_session_key']),
|
||||||
]
|
]
|
||||||
|
@ -672,8 +672,8 @@ class KeyValue(models.Model):
|
||||||
return str(self.key)
|
return str(self.key)
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _("key value association")
|
verbose_name = _('key value association')
|
||||||
verbose_name_plural = _("key value associations")
|
verbose_name_plural = _('key value associations')
|
||||||
|
|
||||||
|
|
||||||
def save_key_values(key, *values):
|
def save_key_values(key, *values):
|
||||||
|
|
|
@ -275,7 +275,7 @@ def iso8601_to_datetime(date_string):
|
||||||
m = re.match(r'(\d+-\d+-\d+T\d+:\d+:\d+)(?:\.\d+)?Z$', date_string)
|
m = re.match(r'(\d+-\d+-\d+T\d+:\d+:\d+)(?:\.\d+)?Z$', date_string)
|
||||||
if not m:
|
if not m:
|
||||||
raise ValueError('Invalid ISO8601 date')
|
raise ValueError('Invalid ISO8601 date')
|
||||||
tm = time.strptime(m.group(1) + 'Z', "%Y-%m-%dT%H:%M:%SZ")
|
tm = time.strptime(m.group(1) + 'Z', '%Y-%m-%dT%H:%M:%SZ')
|
||||||
return datetime.datetime.fromtimestamp(time.mktime(tm))
|
return datetime.datetime.fromtimestamp(time.mktime(tm))
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -50,11 +50,11 @@ def parse_attribute_filters_file(path):
|
||||||
|
|
||||||
|
|
||||||
def fixqname(element, qname):
|
def fixqname(element, qname):
|
||||||
prefix, local = qname.split(":")
|
prefix, local = qname.split(':')
|
||||||
try:
|
try:
|
||||||
return "{%s}%s" % (element.namespaces[prefix], local)
|
return '{%s}%s' % (element.namespaces[prefix], local)
|
||||||
except KeyError:
|
except KeyError:
|
||||||
raise SyntaxError("unknown namespace prefix (%s)" % prefix)
|
raise SyntaxError('unknown namespace prefix (%s)' % prefix)
|
||||||
|
|
||||||
|
|
||||||
def parse_attribute_filter_et(root):
|
def parse_attribute_filter_et(root):
|
||||||
|
|
|
@ -50,7 +50,7 @@ def PreDeserializer(objects, **options):
|
||||||
db = options.pop('using', DEFAULT_DB_ALIAS)
|
db = options.pop('using', DEFAULT_DB_ALIAS)
|
||||||
|
|
||||||
for d in objects:
|
for d in objects:
|
||||||
Model = _get_model(d["model"])
|
Model = _get_model(d['model'])
|
||||||
for pfield in Model._meta.private_fields:
|
for pfield in Model._meta.private_fields:
|
||||||
if not isinstance(pfield, GenericForeignKey):
|
if not isinstance(pfield, GenericForeignKey):
|
||||||
continue
|
continue
|
||||||
|
|
|
@ -334,8 +334,8 @@ REST_FRAMEWORK = {
|
||||||
# Authentic2 Auth SAML
|
# Authentic2 Auth SAML
|
||||||
MELLON_ADAPTER = ('authentic2_auth_saml.adapters.AuthenticAdapter',)
|
MELLON_ADAPTER = ('authentic2_auth_saml.adapters.AuthenticAdapter',)
|
||||||
MELLON_LOOKUP_BY_ATTRIBUTES = [
|
MELLON_LOOKUP_BY_ATTRIBUTES = [
|
||||||
{"saml_attribute": "email", "user_field": "email", "ignore-case": True},
|
{'saml_attribute': 'email', 'user_field': 'email', 'ignore-case': True},
|
||||||
{"saml_attribute": "username", "user_field": "username"},
|
{'saml_attribute': 'username', 'user_field': 'username'},
|
||||||
]
|
]
|
||||||
|
|
||||||
# timeout used in python-requests call, in seconds
|
# timeout used in python-requests call, in seconds
|
||||||
|
@ -372,7 +372,7 @@ DJANGO_RBAC_PERMISSIONS_HIERARCHY = {
|
||||||
|
|
||||||
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
|
FORM_RENDERER = 'django.forms.renderers.TemplatesSetting'
|
||||||
|
|
||||||
SILENCED_SYSTEM_CHECKS = ["auth.W004"]
|
SILENCED_SYSTEM_CHECKS = ['auth.W004']
|
||||||
|
|
||||||
SMS_SENDER = 'EO'
|
SMS_SENDER = 'EO'
|
||||||
SMS_URL = ''
|
SMS_URL = ''
|
||||||
|
|
|
@ -73,19 +73,19 @@ class ExpressionError(ValidationError):
|
||||||
|
|
||||||
|
|
||||||
def is_valid_hostname(hostname):
|
def is_valid_hostname(hostname):
|
||||||
if hostname[-1] == ".":
|
if hostname[-1] == '.':
|
||||||
# strip exactly one dot from the right, if present
|
# strip exactly one dot from the right, if present
|
||||||
hostname = hostname[:-1]
|
hostname = hostname[:-1]
|
||||||
if len(hostname) > 253:
|
if len(hostname) > 253:
|
||||||
return False
|
return False
|
||||||
|
|
||||||
labels = hostname.split(".")
|
labels = hostname.split('.')
|
||||||
|
|
||||||
# the TLD must be not all-numeric
|
# the TLD must be not all-numeric
|
||||||
if re.match(r"[0-9]+$", labels[-1]):
|
if re.match(r'[0-9]+$', labels[-1]):
|
||||||
return False
|
return False
|
||||||
|
|
||||||
allowed = re.compile(r"(?!-)[a-z0-9-]{1,63}(?<!-)$", re.IGNORECASE)
|
allowed = re.compile(r'(?!-)[a-z0-9-]{1,63}(?<!-)$', re.IGNORECASE)
|
||||||
return all(allowed.match(label) for label in labels)
|
return all(allowed.match(label) for label in labels)
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -616,7 +616,7 @@ def login(request, template_name='authentic2/login.html', redirect_field_name=RE
|
||||||
}
|
}
|
||||||
|
|
||||||
# Cancel button
|
# Cancel button
|
||||||
if request.method == "POST" and constants.CANCEL_FIELD_NAME in request.POST:
|
if request.method == 'POST' and constants.CANCEL_FIELD_NAME in request.POST:
|
||||||
return utils_misc.continue_to_next_url(request, params={'cancel': 1})
|
return utils_misc.continue_to_next_url(request, params={'cancel': 1})
|
||||||
|
|
||||||
# Create blocks
|
# Create blocks
|
||||||
|
@ -708,7 +708,7 @@ class ProfileView(HomeURLMixin, cbv.TemplateNamesMixin, TemplateView):
|
||||||
|
|
||||||
request = self.request
|
request = self.request
|
||||||
|
|
||||||
if request.method == "POST":
|
if request.method == 'POST':
|
||||||
for frontend in frontends:
|
for frontend in frontends:
|
||||||
if 'submit-%s' % frontend.get_identifier() in request.POST:
|
if 'submit-%s' % frontend.get_identifier() in request.POST:
|
||||||
form = frontend.form()(data=request.POST)
|
form = frontend.form()(data=request.POST)
|
||||||
|
@ -1049,7 +1049,7 @@ class LoggedInView(View):
|
||||||
logged_in = never_cache(LoggedInView.as_view())
|
logged_in = never_cache(LoggedInView.as_view())
|
||||||
|
|
||||||
|
|
||||||
def csrf_failure_view(request, reason=""):
|
def csrf_failure_view(request, reason=''):
|
||||||
messages.warning(request, _('The page is out of date, it was reloaded for you'))
|
messages.warning(request, _('The page is out of date, it was reloaded for you'))
|
||||||
return HttpResponseRedirect(request.get_full_path())
|
return HttpResponseRedirect(request.get_full_path())
|
||||||
|
|
||||||
|
@ -2004,7 +2004,7 @@ class AccountDeleteView(HomeURLMixin, RecentAuthenticationMixin, TemplateView):
|
||||||
if self.request.user.email_verified:
|
if self.request.user.email_verified:
|
||||||
utils_misc.send_account_deletion_code(self.request, self.request.user)
|
utils_misc.send_account_deletion_code(self.request, self.request.user)
|
||||||
messages.info(
|
messages.info(
|
||||||
request, _("An account deletion validation email has been sent to your email address.")
|
request, _('An account deletion validation email has been sent to your email address.')
|
||||||
)
|
)
|
||||||
elif self.request.user.phone_verified_on and phone:
|
elif self.request.user.phone_verified_on and phone:
|
||||||
try:
|
try:
|
||||||
|
|
|
@ -36,7 +36,7 @@ from django.core.wsgi import get_wsgi_application
|
||||||
# XXX: monkeypatch logging
|
# XXX: monkeypatch logging
|
||||||
from . import logger # pylint: disable=unused-import
|
from . import logger # pylint: disable=unused-import
|
||||||
|
|
||||||
os.environ.setdefault("DJANGO_SETTINGS_MODULE", "authentic2.settings")
|
os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'authentic2.settings')
|
||||||
|
|
||||||
# This application object is used by any WSGI server configured to use this
|
# This application object is used by any WSGI server configured to use this
|
||||||
# file. This includes Django's development server, if the WSGI_APPLICATION
|
# file. This includes Django's development server, if the WSGI_APPLICATION
|
||||||
|
|
|
@ -31,7 +31,7 @@ class Command(BaseCommand):
|
||||||
'''Load LDAP ldif file'''
|
'''Load LDAP ldif file'''
|
||||||
|
|
||||||
can_import_django_settings = True
|
can_import_django_settings = True
|
||||||
requires_system_checks = "__all__"
|
requires_system_checks = '__all__'
|
||||||
help = 'Register an OpenID Connect OP'
|
help = 'Register an OpenID Connect OP'
|
||||||
|
|
||||||
def add_arguments(self, parser):
|
def add_arguments(self, parser):
|
||||||
|
|
|
@ -80,7 +80,7 @@ class AttributeInlineAdmin(admin.TabularInline):
|
||||||
class ServiceAdmin(admin.ModelAdmin):
|
class ServiceAdmin(admin.ModelAdmin):
|
||||||
form = ServiceForm
|
form = ServiceForm
|
||||||
list_display = ('name', 'ou', 'slug', 'urls', 'identifier_attribute')
|
list_display = ('name', 'ou', 'slug', 'urls', 'identifier_attribute')
|
||||||
prepopulated_fields = {"slug": ("name",)}
|
prepopulated_fields = {'slug': ('name',)}
|
||||||
fieldsets = (
|
fieldsets = (
|
||||||
(
|
(
|
||||||
None,
|
None,
|
||||||
|
|
|
@ -58,7 +58,7 @@ class Migration(migrations.Migration):
|
||||||
default=300,
|
default=300,
|
||||||
help_text=(
|
help_text=(
|
||||||
"if iframe logout is used, it's the time between the onload event for this iframe"
|
"if iframe logout is used, it's the time between the onload event for this iframe"
|
||||||
" and the moment we consider its loading to be really finished"
|
' and the moment we consider its loading to be really finished'
|
||||||
),
|
),
|
||||||
verbose_name='iframe logout timeout (ms)',
|
verbose_name='iframe logout timeout (ms)',
|
||||||
),
|
),
|
||||||
|
|
|
@ -49,7 +49,7 @@ class OIDCClientForm(SlugMixin, forms.ModelForm):
|
||||||
]
|
]
|
||||||
labels = {
|
labels = {
|
||||||
'has_api_access': _("Has access to Authentic's synchronization API"),
|
'has_api_access': _("Has access to Authentic's synchronization API"),
|
||||||
'activate_user_profiles': _("Activates user profiles selection"),
|
'activate_user_profiles': _('Activates user profiles selection'),
|
||||||
}
|
}
|
||||||
widgets = {'colour': forms.TextInput(attrs={'type': 'color'})}
|
widgets = {'colour': forms.TextInput(attrs={'type': 'color'})}
|
||||||
|
|
||||||
|
|
|
@ -877,10 +877,10 @@ def test_api_role_add_members(app, api_user, role, member, member_rando2):
|
||||||
else:
|
else:
|
||||||
status = 201
|
status = 201
|
||||||
|
|
||||||
payload = {"data": []}
|
payload = {'data': []}
|
||||||
|
|
||||||
for m in [member, member_rando2, member_rando2]: # test no duplicate
|
for m in [member, member_rando2, member_rando2]: # test no duplicate
|
||||||
payload['data'].append({"uuid": m.uuid})
|
payload['data'].append({'uuid': m.uuid})
|
||||||
|
|
||||||
resp = app.post_json(f'/api/roles/{role.uuid}/relationships/members/', params=payload, status=status)
|
resp = app.post_json(f'/api/roles/{role.uuid}/relationships/members/', params=payload, status=status)
|
||||||
|
|
||||||
|
@ -917,10 +917,10 @@ def test_api_role_remove_members(app, api_user, role, member, member_rando2):
|
||||||
else:
|
else:
|
||||||
status = 200
|
status = 200
|
||||||
|
|
||||||
payload = {"data": []}
|
payload = {'data': []}
|
||||||
|
|
||||||
for m in [member, member_rando2, member_rando2]: # test no duplicate
|
for m in [member, member_rando2, member_rando2]: # test no duplicate
|
||||||
payload['data'].append({"uuid": m.uuid})
|
payload['data'].append({'uuid': m.uuid})
|
||||||
|
|
||||||
resp = app.delete_json(f'/api/roles/{role.uuid}/relationships/members/', params=payload, status=status)
|
resp = app.delete_json(f'/api/roles/{role.uuid}/relationships/members/', params=payload, status=status)
|
||||||
|
|
||||||
|
@ -960,11 +960,11 @@ def test_api_role_set_members(app, api_user, role, member, member_rando2, ou_ran
|
||||||
else:
|
else:
|
||||||
status = 200
|
status = 200
|
||||||
|
|
||||||
payload = {"data": []}
|
payload = {'data': []}
|
||||||
|
|
||||||
role.members.add(user)
|
role.members.add(user)
|
||||||
for m in [member, member_rando2, member_rando2]: # test no duplicate
|
for m in [member, member_rando2, member_rando2]: # test no duplicate
|
||||||
payload['data'].append({"uuid": m.uuid})
|
payload['data'].append({'uuid': m.uuid})
|
||||||
|
|
||||||
resp = app.put_json(f'/api/roles/{role.uuid}/relationships/members/', params=payload, status=status)
|
resp = app.put_json(f'/api/roles/{role.uuid}/relationships/members/', params=payload, status=status)
|
||||||
|
|
||||||
|
@ -1041,21 +1041,21 @@ def test_api_role_members_payload_missing(app, api_user, role):
|
||||||
def test_api_role_members_wrong_payload_types(app, superuser, role_random, member_rando2):
|
def test_api_role_members_wrong_payload_types(app, superuser, role_random, member_rando2):
|
||||||
app.authorization = ('Basic', (superuser.username, superuser.username))
|
app.authorization = ('Basic', (superuser.username, superuser.username))
|
||||||
|
|
||||||
payload = [{"data": [{'uuid': member_rando2.uuid}]}]
|
payload = [{'data': [{'uuid': member_rando2.uuid}]}]
|
||||||
|
|
||||||
resp = app.post_json(f'/api/roles/{role_random.uuid}/relationships/members/', params=payload, status=400)
|
resp = app.post_json(f'/api/roles/{role_random.uuid}/relationships/members/', params=payload, status=400)
|
||||||
|
|
||||||
assert resp.json['result'] == 0
|
assert resp.json['result'] == 0
|
||||||
assert resp.json['errors'] == ['Payload must be a dictionary']
|
assert resp.json['errors'] == ['Payload must be a dictionary']
|
||||||
|
|
||||||
payload = {"data": [[member_rando2.uuid]]}
|
payload = {'data': [[member_rando2.uuid]]}
|
||||||
|
|
||||||
resp = app.post_json(f'/api/roles/{role_random.uuid}/relationships/members/', params=payload, status=400)
|
resp = app.post_json(f'/api/roles/{role_random.uuid}/relationships/members/', params=payload, status=400)
|
||||||
|
|
||||||
assert resp.json['result'] == 0
|
assert resp.json['result'] == 0
|
||||||
assert resp.json['errors'] == ["List elements of the 'data' dict entry must be dictionaries"]
|
assert resp.json['errors'] == ["List elements of the 'data' dict entry must be dictionaries"]
|
||||||
|
|
||||||
payload = {"data": [member_rando2.uuid]}
|
payload = {'data': [member_rando2.uuid]}
|
||||||
|
|
||||||
resp = app.post_json(f'/api/roles/{role_random.uuid}/relationships/members/', params=payload, status=400)
|
resp = app.post_json(f'/api/roles/{role_random.uuid}/relationships/members/', params=payload, status=400)
|
||||||
|
|
||||||
|
@ -1317,14 +1317,14 @@ def test_api_drf_authentication_class(app, admin, user_ou1, oidc_client):
|
||||||
app.authorization = ('Basic', ('foo', 'bar'))
|
app.authorization = ('Basic', ('foo', 'bar'))
|
||||||
resp = app.get(url, status=401)
|
resp = app.get(url, status=401)
|
||||||
assert resp.json['result'] == 0
|
assert resp.json['result'] == 0
|
||||||
assert resp.json['errors'] == "Invalid username/password."
|
assert resp.json['errors'] == 'Invalid username/password.'
|
||||||
# test inactive client
|
# test inactive client
|
||||||
admin.is_active = False
|
admin.is_active = False
|
||||||
admin.save()
|
admin.save()
|
||||||
app.authorization = ('Basic', (admin.username, admin.username))
|
app.authorization = ('Basic', (admin.username, admin.username))
|
||||||
resp = app.get(url, status=401)
|
resp = app.get(url, status=401)
|
||||||
assert resp.json['result'] == 0
|
assert resp.json['result'] == 0
|
||||||
assert resp.json['errors'] == "User inactive or deleted."
|
assert resp.json['errors'] == 'User inactive or deleted.'
|
||||||
# test oidc client unauthorized for user_ou1
|
# test oidc client unauthorized for user_ou1
|
||||||
app.authorization = ('Basic', (oidc_client.username, oidc_client.username))
|
app.authorization = ('Basic', (oidc_client.username, oidc_client.username))
|
||||||
app.get(url, status=404)
|
app.get(url, status=404)
|
||||||
|
@ -1356,7 +1356,7 @@ def test_api_check_password(app, superuser, oidc_client, user_ou1):
|
||||||
payload = {'username': 'whatever', 'password': 'password'}
|
payload = {'username': 'whatever', 'password': 'password'}
|
||||||
resp = app.post_json(reverse('a2-api-check-password'), params=payload, status=200)
|
resp = app.post_json(reverse('a2-api-check-password'), params=payload, status=200)
|
||||||
assert resp.json['result'] == 0
|
assert resp.json['result'] == 0
|
||||||
assert resp.json['errors'] == ["Invalid username/password."]
|
assert resp.json['errors'] == ['Invalid username/password.']
|
||||||
# test with valid credentials
|
# test with valid credentials
|
||||||
payload = {'username': user_ou1.username, 'password': user_ou1.username}
|
payload = {'username': user_ou1.username, 'password': user_ou1.username}
|
||||||
resp = app.post_json(reverse('a2-api-check-password'), params=payload, status=200)
|
resp = app.post_json(reverse('a2-api-check-password'), params=payload, status=200)
|
||||||
|
@ -1600,8 +1600,8 @@ def test_api_post_ou_no_slug(app, superuser):
|
||||||
}
|
}
|
||||||
resp = app.post_json('/api/ous/', params=ou_data, status=400)
|
resp = app.post_json('/api/ous/', params=ou_data, status=400)
|
||||||
assert resp.json['errors']['__all__'] == [
|
assert resp.json['errors']['__all__'] == [
|
||||||
"The fields name must make a unique set.",
|
'The fields name must make a unique set.',
|
||||||
"The fields slug must make a unique set.",
|
'The fields slug must make a unique set.',
|
||||||
]
|
]
|
||||||
# no slug no name
|
# no slug no name
|
||||||
ou_data = {
|
ou_data = {
|
||||||
|
@ -2634,15 +2634,15 @@ def test_api_statistics_list(app, admin):
|
||||||
'id': 'login',
|
'id': 'login',
|
||||||
'filters': [
|
'filters': [
|
||||||
{
|
{
|
||||||
"id": "time_interval",
|
'id': 'time_interval',
|
||||||
"label": "Time interval",
|
'label': 'Time interval',
|
||||||
"options": [
|
'options': [
|
||||||
{"id": "day", "label": "Day"},
|
{'id': 'day', 'label': 'Day'},
|
||||||
{"id": "month", "label": "Month"},
|
{'id': 'month', 'label': 'Month'},
|
||||||
{"id": "year", "label": "Year"},
|
{'id': 'year', 'label': 'Year'},
|
||||||
],
|
],
|
||||||
"required": True,
|
'required': True,
|
||||||
"default": "month",
|
'default': 'month',
|
||||||
},
|
},
|
||||||
{'id': 'service', 'label': 'Service', 'options': []},
|
{'id': 'service', 'label': 'Service', 'options': []},
|
||||||
],
|
],
|
||||||
|
@ -2655,15 +2655,15 @@ def test_api_statistics_list(app, admin):
|
||||||
'id': 'service-login',
|
'id': 'service-login',
|
||||||
'filters': [
|
'filters': [
|
||||||
{
|
{
|
||||||
"id": "time_interval",
|
'id': 'time_interval',
|
||||||
"label": "Time interval",
|
'label': 'Time interval',
|
||||||
"options": [
|
'options': [
|
||||||
{"id": "day", "label": "Day"},
|
{'id': 'day', 'label': 'Day'},
|
||||||
{"id": "month", "label": "Month"},
|
{'id': 'month', 'label': 'Month'},
|
||||||
{"id": "year", "label": "Year"},
|
{'id': 'year', 'label': 'Year'},
|
||||||
],
|
],
|
||||||
"required": True,
|
'required': True,
|
||||||
"default": "month",
|
'default': 'month',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
'deprecated': True,
|
'deprecated': True,
|
||||||
|
@ -2829,7 +2829,7 @@ def test_api_statistics(app, admin, freezer, event_type_name, event_name):
|
||||||
headers = basic_authorization_header(admin)
|
headers = basic_authorization_header(admin)
|
||||||
|
|
||||||
resp = app.get('/api/statistics/login/?time_interval=month', headers=headers)
|
resp = app.get('/api/statistics/login/?time_interval=month', headers=headers)
|
||||||
assert resp.json == {"data": {"series": [], "x_labels": [], "subfilters": []}, "err": 0}
|
assert resp.json == {'data': {'series': [], 'x_labels': [], 'subfilters': []}, 'err': 0}
|
||||||
|
|
||||||
user = User.objects.create(username='john.doe', email='john.doe@example.com', ou=get_default_ou())
|
user = User.objects.create(username='john.doe', email='john.doe@example.com', ou=get_default_ou())
|
||||||
ou = OU.objects.create(name='Second OU', slug='second')
|
ou = OU.objects.create(name='Second OU', slug='second')
|
||||||
|
|
|
@ -765,7 +765,7 @@ def test_fc_authenticator_data_migration(migration, settings):
|
||||||
FcAuthenticator = old_apps.get_model(app, 'FcAuthenticator')
|
FcAuthenticator = old_apps.get_model(app, 'FcAuthenticator')
|
||||||
|
|
||||||
settings.AUTH_FRONTENDS_KWARGS = {
|
settings.AUTH_FRONTENDS_KWARGS = {
|
||||||
"fc": {"priority": 3, "show_condition": "'backoffice' not in login_hint"}
|
'fc': {'priority': 3, 'show_condition': "'backoffice' not in login_hint"}
|
||||||
}
|
}
|
||||||
settings.A2_FC_ENABLE = True
|
settings.A2_FC_ENABLE = True
|
||||||
settings.A2_FC_CLIENT_ID = '211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6k'
|
settings.A2_FC_CLIENT_ID = '211286433e39cce01db448d80181bdfd005554b19cd51b3fe7943f6b3b86ab6k'
|
||||||
|
@ -835,7 +835,7 @@ def test_fc_authenticator_data_migration_bad_settings(migration, settings):
|
||||||
old_apps = migration.before(migrate_from)
|
old_apps = migration.before(migrate_from)
|
||||||
FcAuthenticator = old_apps.get_model(app, 'FcAuthenticator')
|
FcAuthenticator = old_apps.get_model(app, 'FcAuthenticator')
|
||||||
|
|
||||||
settings.AUTH_FRONTENDS_KWARGS = {"fc": {"priority": None, "show_condition": None}}
|
settings.AUTH_FRONTENDS_KWARGS = {'fc': {'priority': None, 'show_condition': None}}
|
||||||
settings.A2_FC_ENABLE = False
|
settings.A2_FC_ENABLE = False
|
||||||
settings.A2_FC_CLIENT_ID = 'x' * 260
|
settings.A2_FC_CLIENT_ID = 'x' * 260
|
||||||
settings.A2_FC_CLIENT_SECRET = None
|
settings.A2_FC_CLIENT_SECRET = None
|
||||||
|
|
|
@ -196,7 +196,7 @@ def test_saml_authenticator_data_migration_bad_settings(migration, settings):
|
||||||
old_apps = migration.before(migrate_from)
|
old_apps = migration.before(migrate_from)
|
||||||
SAMLAuthenticator = old_apps.get_model(app, 'SAMLAuthenticator')
|
SAMLAuthenticator = old_apps.get_model(app, 'SAMLAuthenticator')
|
||||||
|
|
||||||
settings.AUTH_FRONTENDS_KWARGS = {"saml": {"priority": None, "show_condition": None}}
|
settings.AUTH_FRONTENDS_KWARGS = {'saml': {'priority': None, 'show_condition': None}}
|
||||||
settings.MELLON_METADATA_CACHE_TIME = 2**16
|
settings.MELLON_METADATA_CACHE_TIME = 2**16
|
||||||
settings.MELLON_METADATA_HTTP_TIMEOUT = -1
|
settings.MELLON_METADATA_HTTP_TIMEOUT = -1
|
||||||
settings.MELLON_PROVISION = None
|
settings.MELLON_PROVISION = None
|
||||||
|
|
|
@ -27,37 +27,37 @@ from authentic2_idp_oidc.models import OIDCClaim, OIDCClient
|
||||||
from tests import utils
|
from tests import utils
|
||||||
|
|
||||||
JWKSET = {
|
JWKSET = {
|
||||||
"keys": [
|
'keys': [
|
||||||
{
|
{
|
||||||
"qi": "h_zifVD-ChelxZUVxhICNcgGkQz26b-EdIlLY9rN7SX_aD3sLI_JHEHV4Bz3kV5eW8O4qJ8SHhfUdHGK-"
|
'qi': 'h_zifVD-ChelxZUVxhICNcgGkQz26b-EdIlLY9rN7SX_aD3sLI_JHEHV4Bz3kV5eW8O4qJ8SHhfUdHGK-'
|
||||||
"gRH7FVOGoXnXACf47QoXowHzsPLL64wCuZENTl7hIRGLY-BInULkfTQfuiVSMoxPjsVNTMBzMiz0bNjMQyMyvW5xH4",
|
'gRH7FVOGoXnXACf47QoXowHzsPLL64wCuZENTl7hIRGLY-BInULkfTQfuiVSMoxPjsVNTMBzMiz0bNjMQyMyvW5xH4',
|
||||||
"kty": "RSA",
|
'kty': 'RSA',
|
||||||
"d": "pUcL4-LDBy3rqJWip269h5Hd6nLvqjXltfkVe_mL-LwZPHmCrUaj_SX54SnCY3Wyf7kxhoMYUac62lQ71923uJPFFdiavAujbNrtZPq32i4C-"
|
'd': 'pUcL4-LDBy3rqJWip269h5Hd6nLvqjXltfkVe_mL-LwZPHmCrUaj_SX54SnCY3Wyf7kxhoMYUac62lQ71923uJPFFdiavAujbNrtZPq32i4C-'
|
||||||
"1apWXW8OGJr8VoVDqalxj9SAq1G54wbbsaAPrZdyuqy-esNxDqDigfbM-cWgngBBYo5CSsfnmnd05N2cUS26L7QzWbNHwilnBTE9e_J7rK3xUCDKrobv6_LiI-"
|
'1apWXW8OGJr8VoVDqalxj9SAq1G54wbbsaAPrZdyuqy-esNxDqDigfbM-cWgngBBYo5CSsfnmnd05N2cUS26L7QzWbNHwilnBTE9e_J7rK3xUCDKrobv6_LiI-'
|
||||||
"AhMmBHJSrCxjexh0wzfBi_Ntj9BGCcPThDjG8SQvaV-aLNdLfIy2XO3i076RLBB6Hm_yHuAparrwp-pPE48eQdiYjrSAFalz4ojWQ3_ByLA6uAQ",
|
'AhMmBHJSrCxjexh0wzfBi_Ntj9BGCcPThDjG8SQvaV-aLNdLfIy2XO3i076RLBB6Hm_yHuAparrwp-pPE48eQdiYjrSAFalz4ojWQ3_ByLA6uAQ',
|
||||||
"q": "2FvfeWnIlWNUipan7DIBlJrmz5EinJNxrQ-BNwPHrAoIM8qvyC7jPy09YxZs5Y9CMMZSal6C4Nm2LHBFxHU9z1qd5"
|
'q': '2FvfeWnIlWNUipan7DIBlJrmz5EinJNxrQ-BNwPHrAoIM8qvyC7jPy09YxZs5Y9CMMZSal6C4Nm2LHBFxHU9z1qd5'
|
||||||
"XDzbk19G-y1lDqZizVXr876TpiAjuq03rcoMQm8dQru_pVjUdgxR64vKyJ9CaFMAqcpZeEMIqAvzhQG8uE",
|
'XDzbk19G-y1lDqZizVXr876TpiAjuq03rcoMQm8dQru_pVjUdgxR64vKyJ9CaFMAqcpZeEMIqAvzhQG8uE',
|
||||||
"dp": "Kg4HPGpzenhK2ser6nfM1Yt-pkqBbWQotvqsxGptECXpbN7vweupvL5kJPeRrbsXKp9QE7DXTN1sG9puJxMSwtgiv"
|
'dp': 'Kg4HPGpzenhK2ser6nfM1Yt-pkqBbWQotvqsxGptECXpbN7vweupvL5kJPeRrbsXKp9QE7DXTN1sG9puJxMSwtgiv'
|
||||||
"4hr9Va9e9WOC6PMd2VY7tgw5uKMpPLMc5y82PusRhBoRh0SUUsjyQxK9PGtWYnGZXbAoaIYPdMyDlosfqU",
|
'4hr9Va9e9WOC6PMd2VY7tgw5uKMpPLMc5y82PusRhBoRh0SUUsjyQxK9PGtWYnGZXbAoaIYPdMyDlosfqU',
|
||||||
"dq": "QuUNEHYTjZTbo8n2-4FumarXKGBAalbwM8jyc7cYemnTpWfKt8M_gd4T99oMK2IC3h_DhZ3ZK3pE6DKCb76sMLtczH8C1RziTMsATWdc5_zDMtl07O4b-"
|
'dq': 'QuUNEHYTjZTbo8n2-4FumarXKGBAalbwM8jyc7cYemnTpWfKt8M_gd4T99oMK2IC3h_DhZ3ZK3pE6DKCb76sMLtczH8C1RziTMsATWdc5_zDMtl07O4b-'
|
||||||
"ZQ5_g51P8w515pc0JwRzFFi0z3Y2aZdMKgNX1id5SES5nXOshHhICE",
|
'ZQ5_g51P8w515pc0JwRzFFi0z3Y2aZdMKgNX1id5SES5nXOshHhICE',
|
||||||
"n": "0lN6CiJGFD8BSPV_azLoEl6Nq-WlHkU743D5rqvzw1sOaxstMGxAhVk2YIhWwfvapV6XjO_yvc4778VBTELOdjRw6BGUdBJepdwkL__TPyjEVhqMQj9MKhE"
|
'n': '0lN6CiJGFD8BSPV_azLoEl6Nq-WlHkU743D5rqvzw1sOaxstMGxAhVk2YIhWwfvapV6XjO_yvc4778VBTELOdjRw6BGUdBJepdwkL__TPyjEVhqMQj9MKhE'
|
||||||
"U4GUy9w0Lsilb5D01kfrOKpmdcYw4jhcDvb0H4-LZgh1Vk84vF4WaQCUg_AX4drVDQOjoU8kuWIM8gz9w6zEsbIw-gtMRpFwS8ncA0zDX5VfyC77iMxzFftDIP2g"
|
'U4GUy9w0Lsilb5D01kfrOKpmdcYw4jhcDvb0H4-LZgh1Vk84vF4WaQCUg_AX4drVDQOjoU8kuWIM8gz9w6zEsbIw-gtMRpFwS8ncA0zDX5VfyC77iMxzFftDIP2g'
|
||||||
"M5GvdevMzvP9IRkRRBhP9vV4JchBFPHSA9OPJcnySjJJNW6aAJn6P6JasN1z68khjufM09J8UzmLAZYOq7gUG95Ox1KsV-g337Q",
|
'M5GvdevMzvP9IRkRRBhP9vV4JchBFPHSA9OPJcnySjJJNW6aAJn6P6JasN1z68khjufM09J8UzmLAZYOq7gUG95Ox1KsV-g337Q',
|
||||||
"e": "AQAB",
|
'e': 'AQAB',
|
||||||
"p": "-Nyj_Sw3f2HUqSssCZv84y7b3blOtGGAhfYN_JtGfcTQv2bOtxrIUzeonCi-Z_1W4hO10tqxJcOB0ibtDqkDlLhnLaIYOBfriITRFK83EJG5sC-"
|
'p': '-Nyj_Sw3f2HUqSssCZv84y7b3blOtGGAhfYN_JtGfcTQv2bOtxrIUzeonCi-Z_1W4hO10tqxJcOB0ibtDqkDlLhnLaIYOBfriITRFK83EJG5sC-'
|
||||||
"0KTmFzUXFTA2aMc1QgP-Fu6gUfQpPqLgWxhx8EFhkBlBZshKU5-C-385Sco0",
|
'0KTmFzUXFTA2aMc1QgP-Fu6gUfQpPqLgWxhx8EFhkBlBZshKU5-C-385Sco0',
|
||||||
"kid": "46c686ea-7d4e-41cd-a462-2125fc1dee0e",
|
'kid': '46c686ea-7d4e-41cd-a462-2125fc1dee0e',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"kty": "EC",
|
'kty': 'EC',
|
||||||
"d": "wwULaR9UYWZW6U2oEbkz3sO1lhPSj6DyA6e7PiUfhog",
|
'd': 'wwULaR9UYWZW6U2oEbkz3sO1lhPSj6DyA6e7PiUfhog',
|
||||||
"use": "sig",
|
'use': 'sig',
|
||||||
"crv": "P-256",
|
'crv': 'P-256',
|
||||||
"x": "HZMHZkX-63heqA5pvWn-UR7bgcXZNEcQa5wfvG_BzTw",
|
'x': 'HZMHZkX-63heqA5pvWn-UR7bgcXZNEcQa5wfvG_BzTw',
|
||||||
"y": "SUCuwjjiyKvGq5Odr0sjDqjha_CBqks0JQFrR7Ei5OQ",
|
'y': 'SUCuwjjiyKvGq5Odr0sjDqjha_CBqks0JQFrR7Ei5OQ',
|
||||||
"alg": "ES256",
|
'alg': 'ES256',
|
||||||
"kid": "ac85baf4-835b-49b2-8272-ffecce7654c9",
|
'kid': 'ac85baf4-835b-49b2-8272-ffecce7654c9',
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -54,8 +54,8 @@ def test_add_oidc_service_superuser(superuser_app):
|
||||||
assert oidc_client.activate_user_profiles is True
|
assert oidc_client.activate_user_profiles is True
|
||||||
assert resp.location == f'/manage/services/{oidc_client.pk}/'
|
assert resp.location == f'/manage/services/{oidc_client.pk}/'
|
||||||
resp = resp.follow()
|
resp = resp.follow()
|
||||||
assert "Settings" in resp.text
|
assert 'Settings' in resp.text
|
||||||
assert "Delete" in resp.text
|
assert 'Delete' in resp.text
|
||||||
|
|
||||||
|
|
||||||
def test_add_oidc_service_admin(app):
|
def test_add_oidc_service_admin(app):
|
||||||
|
@ -79,8 +79,8 @@ def test_add_oidc_service_admin(app):
|
||||||
assert oidc_client.activate_user_profiles is False
|
assert oidc_client.activate_user_profiles is False
|
||||||
assert resp.location == f'/manage/services/{oidc_client.pk}/'
|
assert resp.location == f'/manage/services/{oidc_client.pk}/'
|
||||||
resp = resp.follow()
|
resp = resp.follow()
|
||||||
assert "Settings" in resp.text
|
assert 'Settings' in resp.text
|
||||||
assert "Delete" in resp.text
|
assert 'Delete' in resp.text
|
||||||
|
|
||||||
|
|
||||||
class TestEdit:
|
class TestEdit:
|
||||||
|
@ -126,8 +126,8 @@ class TestEdit:
|
||||||
resp = form.submit()
|
resp = form.submit()
|
||||||
assert resp.location == '..'
|
assert resp.location == '..'
|
||||||
resp = resp.follow()
|
resp = resp.follow()
|
||||||
assert "New Test" in resp.text
|
assert 'New Test' in resp.text
|
||||||
assert "#ff00ff" in resp.text
|
assert '#ff00ff' in resp.text
|
||||||
|
|
||||||
def test_delete(self, app):
|
def test_delete(self, app):
|
||||||
resp = app.get('/manage/services/')
|
resp = app.get('/manage/services/')
|
||||||
|
@ -210,7 +210,7 @@ class TestEdit:
|
||||||
|
|
||||||
def test_edit_claim(self, app, oidc_client, claim):
|
def test_edit_claim(self, app, oidc_client, claim):
|
||||||
resp = app.get(f'/manage/services/{oidc_client.pk}/settings/')
|
resp = app.get(f'/manage/services/{oidc_client.pk}/settings/')
|
||||||
assert "claim" in resp.text
|
assert 'claim' in resp.text
|
||||||
resp = resp.click('Edit', index=1)
|
resp = resp.click('Edit', index=1)
|
||||||
form = resp.form
|
form = resp.form
|
||||||
form['value'] = 'new value'
|
form['value'] = 'new value'
|
||||||
|
@ -221,7 +221,7 @@ class TestEdit:
|
||||||
|
|
||||||
def test_delete_claim(self, app, oidc_client):
|
def test_delete_claim(self, app, oidc_client):
|
||||||
resp = app.get(f'/manage/services/{oidc_client.pk}/settings/')
|
resp = app.get(f'/manage/services/{oidc_client.pk}/settings/')
|
||||||
assert "claim" in resp.text
|
assert 'claim' in resp.text
|
||||||
resp = resp.click('Delete')
|
resp = resp.click('Delete')
|
||||||
form = resp.form
|
form = resp.form
|
||||||
resp = form.submit()
|
resp = form.submit()
|
||||||
|
|
|
@ -1850,7 +1850,7 @@ def test_consents_view(app, oidc_client, simple_user):
|
||||||
|
|
||||||
utils.login(app, simple_user)
|
utils.login(app, simple_user)
|
||||||
response = app.get(url, status=200)
|
response = app.get(url, status=200)
|
||||||
assert "You have not given any authorization to access your account profile data." in response.text
|
assert 'You have not given any authorization to access your account profile data.' in response.text
|
||||||
|
|
||||||
# create an ou authz
|
# create an ou authz
|
||||||
ou1 = OrganizationalUnit.objects.create(name='Orgunit1', slug='orgunit1')
|
ou1 = OrganizationalUnit.objects.create(name='Orgunit1', slug='orgunit1')
|
||||||
|
@ -1878,7 +1878,7 @@ def test_consents_view(app, oidc_client, simple_user):
|
||||||
)
|
)
|
||||||
|
|
||||||
response = app.get(url, status=200)
|
response = app.get(url, status=200)
|
||||||
assert "You have given authorizations to access your account profile data." in response.text
|
assert 'You have given authorizations to access your account profile data.' in response.text
|
||||||
assert len(response.html.find_all('button', {'class': 'consents--revoke-button'})) == 4
|
assert len(response.html.find_all('button', {'class': 'consents--revoke-button'})) == 4
|
||||||
|
|
||||||
# revoke two service authz
|
# revoke two service authz
|
||||||
|
|
|
@ -371,7 +371,7 @@ def test_login_profile_reversible_sub(app, oidc_client, profile_user, profile_se
|
||||||
id_token = response.json['id_token']
|
id_token = response.json['id_token']
|
||||||
|
|
||||||
jwkset = get_jwkset()
|
jwkset = get_jwkset()
|
||||||
key = jwkset.get_key("ac85baf4-835b-49b2-8272-ffecce7654c9")
|
key = jwkset.get_key('ac85baf4-835b-49b2-8272-ffecce7654c9')
|
||||||
algs = ['ES256']
|
algs = ['ES256']
|
||||||
jwt = JWT(jwt=id_token, key=key, algs=algs)
|
jwt = JWT(jwt=id_token, key=key, algs=algs)
|
||||||
claims = json.loads(jwt.claims)
|
claims = json.loads(jwt.claims)
|
||||||
|
|
|
@ -239,7 +239,7 @@ def test_role_with_permission_export_json(db):
|
||||||
operation=op,
|
operation=op,
|
||||||
ou=ou,
|
ou=ou,
|
||||||
target_ct=ContentType.objects.get_for_model(ContentType),
|
target_ct=ContentType.objects.get_for_model(ContentType),
|
||||||
target_id=ContentType.objects.get(app_label="saml", model="libertyprovider").pk,
|
target_id=ContentType.objects.get(app_label='saml', model='libertyprovider').pk,
|
||||||
)
|
)
|
||||||
role.permissions.add(perm_saml)
|
role.permissions.add(perm_saml)
|
||||||
perm_role = Permission.objects.create(
|
perm_role = Permission.objects.create(
|
||||||
|
|
|
@ -595,7 +595,7 @@ def test_sso(app, caplog, code, oidc_provider, oidc_provider_jwkset, hooks):
|
||||||
)
|
)
|
||||||
utils.assert_event(
|
utils.assert_event(
|
||||||
'user.login.failure',
|
'user.login.failure',
|
||||||
reason="auth_oidc: error received some_other_error (prompt: whatever)",
|
reason='auth_oidc: error received some_other_error (prompt: whatever)',
|
||||||
)
|
)
|
||||||
assert len(hooks.auth_oidc_backend_modify_user) == 0
|
assert len(hooks.auth_oidc_backend_modify_user) == 0
|
||||||
with utils.check_log(caplog, 'created user'):
|
with utils.check_log(caplog, 'created user'):
|
||||||
|
|
|
@ -56,7 +56,7 @@ def test_change_phone(app, nomail_user, user_ou1, phone_activated_authn, setting
|
||||||
path='/accounts/change-phone/',
|
path='/accounts/change-phone/',
|
||||||
password=nomail_user.username,
|
password=nomail_user.username,
|
||||||
)
|
)
|
||||||
assert "Your current phone number is +33122446688." in resp.text
|
assert 'Your current phone number is +33122446688.' in resp.text
|
||||||
|
|
||||||
resp.form.set('phone_1', '122446666')
|
resp.form.set('phone_1', '122446666')
|
||||||
resp.form.set('password', nomail_user.username)
|
resp.form.set('password', nomail_user.username)
|
||||||
|
@ -99,7 +99,7 @@ def test_change_phone_nondefault_attribute(app, nomail_user, user_ou1, phone_act
|
||||||
path='/accounts/change-phone/',
|
path='/accounts/change-phone/',
|
||||||
password=nomail_user.username,
|
password=nomail_user.username,
|
||||||
)
|
)
|
||||||
assert "Your current phone number is +33122444444." in resp.text
|
assert 'Your current phone number is +33122444444.' in resp.text
|
||||||
|
|
||||||
resp.form.set('phone_1', '122666666')
|
resp.form.set('phone_1', '122666666')
|
||||||
resp.form.set('password', nomail_user.username)
|
resp.form.set('password', nomail_user.username)
|
||||||
|
@ -238,7 +238,7 @@ def test_change_phone_identifier_attribute_changed(
|
||||||
path='/accounts/change-phone/',
|
path='/accounts/change-phone/',
|
||||||
password=nomail_user.username,
|
password=nomail_user.username,
|
||||||
)
|
)
|
||||||
assert "Your current phone number is +33122446688." in resp.text
|
assert 'Your current phone number is +33122446688.' in resp.text
|
||||||
|
|
||||||
resp.form.set('phone_1', '122446666')
|
resp.form.set('phone_1', '122446666')
|
||||||
resp.form.set('password', nomail_user.username)
|
resp.form.set('password', nomail_user.username)
|
||||||
|
@ -274,7 +274,7 @@ def test_change_phone_authn_deactivated(app, nomail_user, user_ou1, phone_activa
|
||||||
phone_activated_authn.save()
|
phone_activated_authn.save()
|
||||||
|
|
||||||
resp = app.get('/accounts/change-phone/')
|
resp = app.get('/accounts/change-phone/')
|
||||||
assert "Your current phone number is +33122446688." in resp.text
|
assert 'Your current phone number is +33122446688.' in resp.text
|
||||||
|
|
||||||
resp.form.set('phone_1', '122446666')
|
resp.form.set('phone_1', '122446666')
|
||||||
resp.form.set('password', nomail_user.username)
|
resp.form.set('password', nomail_user.username)
|
||||||
|
|
|
@ -466,7 +466,7 @@ def test_clean_unused_account_always_alert(db, simple_user, mailoutbox, freezer)
|
||||||
assert len(mailoutbox) == 1
|
assert len(mailoutbox) == 1
|
||||||
|
|
||||||
|
|
||||||
@pytest.mark.parametrize("deletion_delay", [730, 500, 65])
|
@pytest.mark.parametrize('deletion_delay', [730, 500, 65])
|
||||||
def test_clean_unused_account_displayed_message(simple_user, mailoutbox, deletion_delay):
|
def test_clean_unused_account_displayed_message(simple_user, mailoutbox, deletion_delay):
|
||||||
simple_user.ou.clean_unused_accounts_alert = deletion_delay - 30
|
simple_user.ou.clean_unused_accounts_alert = deletion_delay - 30
|
||||||
simple_user.ou.clean_unused_accounts_deletion = deletion_delay
|
simple_user.ou.clean_unused_accounts_deletion = deletion_delay
|
||||||
|
|
|
@ -323,7 +323,7 @@ def test_role_deserializer_parenting_non_existing_parent(db):
|
||||||
with pytest.raises(ValidationError) as excinfo:
|
with pytest.raises(ValidationError) as excinfo:
|
||||||
rd.parentings()
|
rd.parentings()
|
||||||
|
|
||||||
assert "Could not find parent role" in str(excinfo.value)
|
assert 'Could not find parent role' in str(excinfo.value)
|
||||||
|
|
||||||
|
|
||||||
def test_role_deserializer_emails(db):
|
def test_role_deserializer_emails(db):
|
||||||
|
@ -348,14 +348,14 @@ def test_role_deserializer_permissions(db):
|
||||||
other_role_dict = {'name': 'other role', 'slug': 'other-role-slug', 'uuid': get_hex_uuid(), 'ou': ou}
|
other_role_dict = {'name': 'other role', 'slug': 'other-role-slug', 'uuid': get_hex_uuid(), 'ou': ou}
|
||||||
other_role = Role.objects.create(**other_role_dict)
|
other_role = Role.objects.create(**other_role_dict)
|
||||||
other_role_dict['permisison'] = {
|
other_role_dict['permisison'] = {
|
||||||
"operation": {"slug": "admin"},
|
'operation': {'slug': 'admin'},
|
||||||
"ou": {"slug": "default", "name": "Collectivit\u00e9 par d\u00e9faut"},
|
'ou': {'slug': 'default', 'name': 'Collectivit\u00e9 par d\u00e9faut'},
|
||||||
'target_ct': {'app_label': 'a2_rbac', 'model': 'role'},
|
'target_ct': {'app_label': 'a2_rbac', 'model': 'role'},
|
||||||
"target": {
|
'target': {
|
||||||
"slug": "role-deux",
|
'slug': 'role-deux',
|
||||||
"ou": {"slug": "default", "name": "Collectivit\u00e9 par d\u00e9faut"},
|
'ou': {'slug': 'default', 'name': 'Collectivit\u00e9 par d\u00e9faut'},
|
||||||
"service": None,
|
'service': None,
|
||||||
"name": "role deux",
|
'name': 'role deux',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
some_role_dict = {
|
some_role_dict = {
|
||||||
|
@ -370,7 +370,7 @@ def test_role_deserializer_permissions(db):
|
||||||
'operation': {'slug': 'add'},
|
'operation': {'slug': 'add'},
|
||||||
'ou': None,
|
'ou': None,
|
||||||
'target_ct': {'app_label': 'a2_rbac', 'model': 'role'},
|
'target_ct': {'app_label': 'a2_rbac', 'model': 'role'},
|
||||||
'target': {"slug": 'other-role-slug', 'ou': {'slug': 'some-ou'}, 'service': None},
|
'target': {'slug': 'other-role-slug', 'ou': {'slug': 'some-ou'}, 'service': None},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -405,14 +405,14 @@ def test_permission_on_role(db):
|
||||||
some_role_dict = {'name': 'some role', 'slug': 'some-role-slug', 'ou': None, 'service': None}
|
some_role_dict = {'name': 'some role', 'slug': 'some-role-slug', 'ou': None, 'service': None}
|
||||||
some_role_dict['permissions'] = [
|
some_role_dict['permissions'] = [
|
||||||
{
|
{
|
||||||
"operation": {"slug": "admin"},
|
'operation': {'slug': 'admin'},
|
||||||
"ou": {"slug": "perm-ou", "name": "perm-ou"},
|
'ou': {'slug': 'perm-ou', 'name': 'perm-ou'},
|
||||||
'target_ct': {'app_label': 'a2_rbac', 'model': 'role'},
|
'target_ct': {'app_label': 'a2_rbac', 'model': 'role'},
|
||||||
"target": {
|
'target': {
|
||||||
"slug": "perm-role",
|
'slug': 'perm-role',
|
||||||
"ou": {"slug": "perm-ou", "name": "perm ou"},
|
'ou': {'slug': 'perm-ou', 'name': 'perm ou'},
|
||||||
"service": None,
|
'service': None,
|
||||||
"name": "perm role",
|
'name': 'perm role',
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -433,10 +433,10 @@ def test_permission_on_contentype(db):
|
||||||
some_role_dict = {'name': 'some role', 'slug': 'some-role-slug', 'ou': None, 'service': None}
|
some_role_dict = {'name': 'some role', 'slug': 'some-role-slug', 'ou': None, 'service': None}
|
||||||
some_role_dict['permissions'] = [
|
some_role_dict['permissions'] = [
|
||||||
{
|
{
|
||||||
"operation": {"slug": "admin"},
|
'operation': {'slug': 'admin'},
|
||||||
"ou": {"slug": "perm-ou", "name": "perm-ou"},
|
'ou': {'slug': 'perm-ou', 'name': 'perm-ou'},
|
||||||
'target_ct': {"model": "contenttype", "app_label": "contenttypes"},
|
'target_ct': {'model': 'contenttype', 'app_label': 'contenttypes'},
|
||||||
"target": {"model": "logentry", "app_label": "admin"},
|
'target': {'model': 'logentry', 'app_label': 'admin'},
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -82,9 +82,9 @@ def test_import_site_cmd_infos_on_stdout(db, monkeypatch, capsys, json_fixture):
|
||||||
management.call_command('import_site', json_fixture(content))
|
management.call_command('import_site', json_fixture(content))
|
||||||
|
|
||||||
out, dummy = capsys.readouterr()
|
out, dummy = capsys.readouterr()
|
||||||
assert "Real run" in out
|
assert 'Real run' in out
|
||||||
assert "1 roles created" in out
|
assert '1 roles created' in out
|
||||||
assert "0 roles updated" in out
|
assert '0 roles updated' in out
|
||||||
|
|
||||||
|
|
||||||
def test_import_site_transaction_rollback_on_error(db, monkeypatch, capsys, json_fixture):
|
def test_import_site_transaction_rollback_on_error(db, monkeypatch, capsys, json_fixture):
|
||||||
|
|
|
@ -63,15 +63,15 @@ OBJECTGUID_B64 = base64.b64encode(OBJECTGUID_RAW).decode()
|
||||||
CN_INCOMPLETE = 'Jean Dupond'
|
CN_INCOMPLETE = 'Jean Dupond'
|
||||||
DN_INCOMPLETE = 'cn=%s,o=ôrga' % escape_dn_chars(CN_INCOMPLETE)
|
DN_INCOMPLETE = 'cn=%s,o=ôrga' % escape_dn_chars(CN_INCOMPLETE)
|
||||||
|
|
||||||
EO_O = "EO"
|
EO_O = 'EO'
|
||||||
EO_STREET = "169 rue du Chateau"
|
EO_STREET = '169 rue du Chateau'
|
||||||
EO_POSTALCODE = "75014"
|
EO_POSTALCODE = '75014'
|
||||||
EO_CITY = "PARIS"
|
EO_CITY = 'PARIS'
|
||||||
|
|
||||||
EE_O = "EE"
|
EE_O = 'EE'
|
||||||
EE_STREET = "44 rue de l'Ouest"
|
EE_STREET = "44 rue de l'Ouest"
|
||||||
EE_POSTALCODE = "75014"
|
EE_POSTALCODE = '75014'
|
||||||
EE_CITY = "PARIS"
|
EE_CITY = 'PARIS'
|
||||||
|
|
||||||
base_dir = os.path.dirname(__file__)
|
base_dir = os.path.dirname(__file__)
|
||||||
key_file = os.path.join(base_dir, 'key.pem')
|
key_file = os.path.join(base_dir, 'key.pem')
|
||||||
|
@ -1521,7 +1521,7 @@ pwdSafeModify: FALSE
|
||||||
|
|
||||||
for _ in range(pwdMaxFailure):
|
for _ in range(pwdMaxFailure):
|
||||||
assert authenticate(username=USERNAME, password='incorrect') is None
|
assert authenticate(username=USERNAME, password='incorrect') is None
|
||||||
assert "failed to login" in caplog.text
|
assert 'failed to login' in caplog.text
|
||||||
|
|
||||||
|
|
||||||
def test_authenticate_ppolicy_pwdMaxFailure(slapd_ppolicy, settings, db, caplog):
|
def test_authenticate_ppolicy_pwdMaxFailure(slapd_ppolicy, settings, db, caplog):
|
||||||
|
@ -2197,19 +2197,19 @@ def test_config_to_lowercase(db):
|
||||||
del config_normalized[key]
|
del config_normalized[key]
|
||||||
|
|
||||||
assert config_normalized == {
|
assert config_normalized == {
|
||||||
"fname_field": "givenname",
|
'fname_field': 'givenname',
|
||||||
"lname_field": "surname",
|
'lname_field': 'surname',
|
||||||
"email_field": "email",
|
'email_field': 'email',
|
||||||
"attributes": ["zob", "coin"],
|
'attributes': ['zob', 'coin'],
|
||||||
"mandatory_attributes_values": {"xxx": ["A"]},
|
'mandatory_attributes_values': {'xxx': ['A']},
|
||||||
"member_of_attribute": "memberof",
|
'member_of_attribute': 'memberof',
|
||||||
"group_mapping": [["cn=coin,ou=groups,dc=coin,dc=fr", ["Group 1"]]],
|
'group_mapping': [['cn=coin,ou=groups,dc=coin,dc=fr', ['Group 1']]],
|
||||||
"group_to_role_mapping": [["cn=coin,ou=groups,dc=coin,dc=fr", ["Group 1"]]],
|
'group_to_role_mapping': [['cn=coin,ou=groups,dc=coin,dc=fr', ['Group 1']]],
|
||||||
"attribute_mappings": [
|
'attribute_mappings': [
|
||||||
["xxx", "yyy"],
|
['xxx', 'yyy'],
|
||||||
],
|
],
|
||||||
"external_id_tuples": [
|
'external_id_tuples': [
|
||||||
["a", "b", "c"],
|
['a', 'b', 'c'],
|
||||||
],
|
],
|
||||||
'user_attributes': [
|
'user_attributes': [
|
||||||
{
|
{
|
||||||
|
|
|
@ -580,7 +580,7 @@ def test_password_authenticator_data_migration(migration, settings):
|
||||||
LoginPasswordAuthenticator = old_apps.get_model(app, 'LoginPasswordAuthenticator')
|
LoginPasswordAuthenticator = old_apps.get_model(app, 'LoginPasswordAuthenticator')
|
||||||
|
|
||||||
settings.AUTH_FRONTENDS_KWARGS = {
|
settings.AUTH_FRONTENDS_KWARGS = {
|
||||||
"password": {"priority": -1, "show_condition": "'backoffice' not in login_hint"}
|
'password': {'priority': -1, 'show_condition': "'backoffice' not in login_hint"}
|
||||||
}
|
}
|
||||||
settings.A2_LOGIN_FORM_OU_SELECTOR = True
|
settings.A2_LOGIN_FORM_OU_SELECTOR = True
|
||||||
settings.A2_AUTH_PASSWORD_ENABLE = False
|
settings.A2_AUTH_PASSWORD_ENABLE = False
|
||||||
|
|
|
@ -838,18 +838,18 @@ def test_manager_site_import(app, db, superuser):
|
||||||
site_export = {
|
site_export = {
|
||||||
'roles': [
|
'roles': [
|
||||||
{
|
{
|
||||||
"description": "",
|
'description': '',
|
||||||
"service": None,
|
'service': None,
|
||||||
"name": "basic",
|
'name': 'basic',
|
||||||
"attributes": [],
|
'attributes': [],
|
||||||
"ou": {
|
'ou': {
|
||||||
"slug": "default",
|
'slug': 'default',
|
||||||
"uuid": "ba60d9e6c2874636883bdd604b23eab2",
|
'uuid': 'ba60d9e6c2874636883bdd604b23eab2',
|
||||||
"name": "Collectivit\u00e9 par d\u00e9faut",
|
'name': 'Collectivit\u00e9 par d\u00e9faut',
|
||||||
},
|
},
|
||||||
"external_id": "",
|
'external_id': '',
|
||||||
"slug": "basic",
|
'slug': 'basic',
|
||||||
"uuid": "6eb7bbf64bf547119120f925f0e560ac",
|
'uuid': '6eb7bbf64bf547119120f925f0e560ac',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
@ -867,14 +867,14 @@ def test_manager_site_import_error(app, db, superuser):
|
||||||
site_export = {
|
site_export = {
|
||||||
'roles': [
|
'roles': [
|
||||||
{
|
{
|
||||||
"description": "",
|
'description': '',
|
||||||
"service": None,
|
'service': None,
|
||||||
"name": "basic",
|
'name': 'basic',
|
||||||
"attributes": [],
|
'attributes': [],
|
||||||
"ou": {"slug": "unkown-ou", "uuid": "ba60d9e6c2874636883bdd604b23eab2", "name": "unkown ou"},
|
'ou': {'slug': 'unkown-ou', 'uuid': 'ba60d9e6c2874636883bdd604b23eab2', 'name': 'unkown ou'},
|
||||||
"external_id": "",
|
'external_id': '',
|
||||||
"slug": "basic",
|
'slug': 'basic',
|
||||||
"uuid": "6eb7bbf64bf547119120f925f0e560ac",
|
'uuid': '6eb7bbf64bf547119120f925f0e560ac',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
|
|
|
@ -277,7 +277,7 @@ def test_edit(superuser, app, ou1, ou2):
|
||||||
response = form.submit()
|
response = form.submit()
|
||||||
assert (
|
assert (
|
||||||
response.pyquery('.error').text()
|
response.pyquery('.error').text()
|
||||||
== "The following roles do not belong to organizational unit OU2: role-1, role-3."
|
== 'The following roles do not belong to organizational unit OU2: role-1, role-3.'
|
||||||
)
|
)
|
||||||
response.form.set('ou', ou2.id)
|
response.form.set('ou', ou2.id)
|
||||||
response.form['apiclient_roles'].force_value([])
|
response.form['apiclient_roles'].force_value([])
|
||||||
|
|
|
@ -100,20 +100,20 @@ def test_authenticators_password(app, superuser_or_admin, settings):
|
||||||
|
|
||||||
resp.form['show_condition'] = '}'
|
resp.form['show_condition'] = '}'
|
||||||
resp = resp.form.submit()
|
resp = resp.form.submit()
|
||||||
assert "could not parse expression: unmatched" in resp.text
|
assert 'could not parse expression: unmatched' in resp.text
|
||||||
|
|
||||||
resp.form['show_condition'] = "'backoffice' in login_hint or remote_addr == '1.2.3.4'"
|
resp.form['show_condition'] = "'backoffice' in login_hint or remote_addr == '1.2.3.4'"
|
||||||
resp = resp.form.submit().follow()
|
resp = resp.form.submit().follow()
|
||||||
assert 'Click "Edit" to change configuration.' not in resp.text
|
assert 'Click "Edit" to change configuration.' not in resp.text
|
||||||
if DJ_VERSION[0] <= 2:
|
if DJ_VERSION[0] <= 2:
|
||||||
assert (
|
assert (
|
||||||
"Show condition: 'backoffice' in login_hint or remote_addr == '1.2.3.4'"
|
'Show condition: 'backoffice' in login_hint or remote_addr == '1.2.3.4''
|
||||||
in resp.text
|
in resp.text
|
||||||
)
|
)
|
||||||
else:
|
else:
|
||||||
# html-rendered quote characters change in django 3 onwards…
|
# html-rendered quote characters change in django 3 onwards…
|
||||||
assert (
|
assert (
|
||||||
"Show condition: 'backoffice' in login_hint or remote_addr == '1.2.3.4'"
|
'Show condition: 'backoffice' in login_hint or remote_addr == '1.2.3.4''
|
||||||
in resp.text
|
in resp.text
|
||||||
)
|
)
|
||||||
assert_event('authenticator.edit', user=superuser_or_admin, session=app.session)
|
assert_event('authenticator.edit', user=superuser_or_admin, session=app.session)
|
||||||
|
@ -621,7 +621,7 @@ def test_authenticators_saml(app, superuser, ou1, ou2):
|
||||||
|
|
||||||
authenticator.refresh_from_db()
|
authenticator.refresh_from_db()
|
||||||
assert authenticator.attribute_mapping == [
|
assert authenticator.attribute_mapping == [
|
||||||
{"attribute": "email", "saml_attribute": "mail", "mandatory": False}
|
{'attribute': 'email', 'saml_attribute': 'mail', 'mandatory': False}
|
||||||
]
|
]
|
||||||
|
|
||||||
resp = resp.click('Edit')
|
resp = resp.click('Edit')
|
||||||
|
|
|
@ -45,24 +45,24 @@ def test_journal_authorization(app, db, simple_user, admin):
|
||||||
|
|
||||||
@pytest.fixture(autouse=True)
|
@pytest.fixture(autouse=True)
|
||||||
def events(db, superuser, freezer):
|
def events(db, superuser, freezer):
|
||||||
session1 = Session(session_key="1234")
|
session1 = Session(session_key='1234')
|
||||||
session2 = Session(session_key="abcd")
|
session2 = Session(session_key='abcd')
|
||||||
|
|
||||||
ou = get_default_ou()
|
ou = get_default_ou()
|
||||||
user = User.objects.create(
|
user = User.objects.create(
|
||||||
username="user", email="user@example.com", ou=ou, uuid="1" * 32, first_name='Johnny', last_name='doe'
|
username='user', email='user@example.com', ou=ou, uuid='1' * 32, first_name='Johnny', last_name='doe'
|
||||||
)
|
)
|
||||||
profile_type = ProfileType.objects.create(name='One Type', slug='one-type')
|
profile_type = ProfileType.objects.create(name='One Type', slug='one-type')
|
||||||
profile = Profile.objects.create(user=user, profile_type=profile_type, identifier='aaa')
|
profile = Profile.objects.create(user=user, profile_type=profile_type, identifier='aaa')
|
||||||
agent = User.objects.create(username="agent", email="agent@example.com", ou=ou, uuid="2" * 32)
|
agent = User.objects.create(username='agent', email='agent@example.com', ou=ou, uuid='2' * 32)
|
||||||
role_user = Role.objects.create(name="role1", ou=ou)
|
role_user = Role.objects.create(name='role1', ou=ou)
|
||||||
role_agent = Role.objects.create(name="role2", ou=ou)
|
role_agent = Role.objects.create(name='role2', ou=ou)
|
||||||
service = Service.objects.create(name="service")
|
service = Service.objects.create(name='service')
|
||||||
authenticator = LoginPasswordAuthenticator.objects.create(slug='test')
|
authenticator = LoginPasswordAuthenticator.objects.create(slug='test')
|
||||||
saml_authenticator = SAMLAuthenticator.objects.create(slug='saml')
|
saml_authenticator = SAMLAuthenticator.objects.create(slug='saml')
|
||||||
set_attribute_action = SetAttributeAction.objects.create(authenticator=saml_authenticator)
|
set_attribute_action = SetAttributeAction.objects.create(authenticator=saml_authenticator)
|
||||||
|
|
||||||
deleted_user = User.objects.create(username="deleted", email="deleted@example.com", ou=ou, uuid="3" * 32)
|
deleted_user = User.objects.create(username='deleted', email='deleted@example.com', ou=ou, uuid='3' * 32)
|
||||||
|
|
||||||
class EventFactory:
|
class EventFactory:
|
||||||
date = make_aware(datetime.datetime(2020, 1, 1))
|
date = make_aware(datetime.datetime(2020, 1, 1))
|
||||||
|
@ -70,133 +70,133 @@ def events(db, superuser, freezer):
|
||||||
def __call__(self, name, **kwargs):
|
def __call__(self, name, **kwargs):
|
||||||
freezer.move_to(self.date)
|
freezer.move_to(self.date)
|
||||||
journal.record(name, **kwargs)
|
journal.record(name, **kwargs)
|
||||||
assert Event.objects.latest("timestamp").type.name == name
|
assert Event.objects.latest('timestamp').type.name == name
|
||||||
self.date += datetime.timedelta(hours=1)
|
self.date += datetime.timedelta(hours=1)
|
||||||
|
|
||||||
make = EventFactory()
|
make = EventFactory()
|
||||||
make("user.registration.request", email=user.email)
|
make('user.registration.request', email=user.email)
|
||||||
make(
|
make(
|
||||||
"user.registration",
|
'user.registration',
|
||||||
user=user,
|
user=user,
|
||||||
session=session1,
|
session=session1,
|
||||||
service=service,
|
service=service,
|
||||||
how="france-connect",
|
how='france-connect',
|
||||||
)
|
)
|
||||||
make("user.logout", user=user, session=session1)
|
make('user.logout', user=user, session=session1)
|
||||||
|
|
||||||
make("user.login.failure", service=service, authenticator=saml_authenticator, username="user")
|
make('user.login.failure', service=service, authenticator=saml_authenticator, username='user')
|
||||||
make("user.login.failure", authenticator=saml_authenticator, username="agent")
|
make('user.login.failure', authenticator=saml_authenticator, username='agent')
|
||||||
make("user.login", user=user, session=session1, how="password")
|
make('user.login', user=user, session=session1, how='password')
|
||||||
make("user.password.change", user=user, session=session1)
|
make('user.password.change', user=user, session=session1)
|
||||||
edit_profile_form = mock.Mock(spec=["instance", "initial", "changed_data", "cleaned_data"])
|
edit_profile_form = mock.Mock(spec=['instance', 'initial', 'changed_data', 'cleaned_data'])
|
||||||
edit_profile_form.initial = {'email': "user@example.com", 'first_name': "John"}
|
edit_profile_form.initial = {'email': 'user@example.com', 'first_name': 'John'}
|
||||||
edit_profile_form.changed_data = ["first_name"]
|
edit_profile_form.changed_data = ['first_name']
|
||||||
edit_profile_form.cleaned_data = {'first_name': "Jane"}
|
edit_profile_form.cleaned_data = {'first_name': 'Jane'}
|
||||||
make("user.profile.edit", user=user, session=session1, form=edit_profile_form)
|
make('user.profile.edit', user=user, session=session1, form=edit_profile_form)
|
||||||
make("user.service.sso.authorization", user=user, session=session1, service=service)
|
make('user.service.sso.authorization', user=user, session=session1, service=service)
|
||||||
make("user.service.sso", user=user, session=session1, service=service, how="password")
|
make('user.service.sso', user=user, session=session1, service=service, how='password')
|
||||||
make("user.service.sso.unauthorization", user=user, session=session1, service=service)
|
make('user.service.sso.unauthorization', user=user, session=session1, service=service)
|
||||||
make("user.deletion", user=user, session=session1, service=service)
|
make('user.deletion', user=user, session=session1, service=service)
|
||||||
|
|
||||||
make("user.password.reset.request", email="USER@example.com", user=user)
|
make('user.password.reset.request', email='USER@example.com', user=user)
|
||||||
make("user.password.reset.failure", email="USER@example.com")
|
make('user.password.reset.failure', email='USER@example.com')
|
||||||
make("user.password.reset", user=user)
|
make('user.password.reset', user=user)
|
||||||
|
|
||||||
make("user.login", user=agent, session=session2, how="saml")
|
make('user.login', user=agent, session=session2, how='saml')
|
||||||
|
|
||||||
create_form = mock.Mock(spec=["instance"])
|
create_form = mock.Mock(spec=['instance'])
|
||||||
create_form.instance = user
|
create_form.instance = user
|
||||||
make("manager.user.creation", user=agent, session=session2, form=create_form)
|
make('manager.user.creation', user=agent, session=session2, form=create_form)
|
||||||
|
|
||||||
edit_form = mock.Mock(spec=["instance", "initial", "changed_data", "cleaned_data"])
|
edit_form = mock.Mock(spec=['instance', 'initial', 'changed_data', 'cleaned_data'])
|
||||||
edit_form.instance = user
|
edit_form.instance = user
|
||||||
edit_form.initial = {'email': "user@example.com", 'first_name': "John"}
|
edit_form.initial = {'email': 'user@example.com', 'first_name': 'John'}
|
||||||
edit_form.changed_data = ["first_name"]
|
edit_form.changed_data = ['first_name']
|
||||||
edit_form.cleaned_data = {'first_name': "Jane"}
|
edit_form.cleaned_data = {'first_name': 'Jane'}
|
||||||
make("manager.user.profile.edit", user=agent, session=session2, form=edit_form)
|
make('manager.user.profile.edit', user=agent, session=session2, form=edit_form)
|
||||||
|
|
||||||
change_email_form = mock.Mock(spec=["instance", "cleaned_data"])
|
change_email_form = mock.Mock(spec=['instance', 'cleaned_data'])
|
||||||
change_email_form.instance = user
|
change_email_form.instance = user
|
||||||
change_email_form.cleaned_data = {'new_email': "jane@example.com"}
|
change_email_form.cleaned_data = {'new_email': 'jane@example.com'}
|
||||||
make(
|
make(
|
||||||
"manager.user.email.change.request",
|
'manager.user.email.change.request',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
form=change_email_form,
|
form=change_email_form,
|
||||||
)
|
)
|
||||||
|
|
||||||
password_change_form = mock.Mock(spec=["instance", "cleaned_data"])
|
password_change_form = mock.Mock(spec=['instance', 'cleaned_data'])
|
||||||
password_change_form.instance = user
|
password_change_form.instance = user
|
||||||
password_change_form.cleaned_data = {'generate_password': False, 'send_mail': False}
|
password_change_form.cleaned_data = {'generate_password': False, 'send_mail': False}
|
||||||
make(
|
make(
|
||||||
"manager.user.password.change",
|
'manager.user.password.change',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
form=password_change_form,
|
form=password_change_form,
|
||||||
)
|
)
|
||||||
|
|
||||||
password_change_form.cleaned_data["send_mail"] = True
|
password_change_form.cleaned_data['send_mail'] = True
|
||||||
make(
|
make(
|
||||||
"manager.user.password.change",
|
'manager.user.password.change',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
form=password_change_form,
|
form=password_change_form,
|
||||||
)
|
)
|
||||||
|
|
||||||
make(
|
make(
|
||||||
"manager.user.password.reset.request",
|
'manager.user.password.reset.request',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
target_user=user,
|
target_user=user,
|
||||||
)
|
)
|
||||||
|
|
||||||
make(
|
make(
|
||||||
"manager.user.password.change.force",
|
'manager.user.password.change.force',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
target_user=user,
|
target_user=user,
|
||||||
)
|
)
|
||||||
make(
|
make(
|
||||||
"manager.user.password.change.unforce",
|
'manager.user.password.change.unforce',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
target_user=user,
|
target_user=user,
|
||||||
)
|
)
|
||||||
|
|
||||||
make("manager.user.activation", user=agent, session=session2, target_user=user)
|
make('manager.user.activation', user=agent, session=session2, target_user=user)
|
||||||
make("manager.user.deactivation", user=agent, session=session2, target_user=user)
|
make('manager.user.deactivation', user=agent, session=session2, target_user=user)
|
||||||
make("manager.user.deletion", user=agent, session=session2, target_user=user)
|
make('manager.user.deletion', user=agent, session=session2, target_user=user)
|
||||||
make(
|
make(
|
||||||
"manager.user.sso.authorization.deletion",
|
'manager.user.sso.authorization.deletion',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
service=service,
|
service=service,
|
||||||
target_user=user,
|
target_user=user,
|
||||||
)
|
)
|
||||||
|
|
||||||
make("manager.role.creation", user=agent, session=session2, role=role_user)
|
make('manager.role.creation', user=agent, session=session2, role=role_user)
|
||||||
role_edit_form = mock.Mock(spec=["instance", "initial", "changed_data", "cleaned_data"])
|
role_edit_form = mock.Mock(spec=['instance', 'initial', 'changed_data', 'cleaned_data'])
|
||||||
role_edit_form.instance = role_user
|
role_edit_form.instance = role_user
|
||||||
role_edit_form.initial = {'name': role_user.name}
|
role_edit_form.initial = {'name': role_user.name}
|
||||||
role_edit_form.changed_data = ["name"]
|
role_edit_form.changed_data = ['name']
|
||||||
role_edit_form.cleaned_data = {'name': "changed role name"}
|
role_edit_form.cleaned_data = {'name': 'changed role name'}
|
||||||
make(
|
make(
|
||||||
"manager.role.edit",
|
'manager.role.edit',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
role=role_user,
|
role=role_user,
|
||||||
form=role_edit_form,
|
form=role_edit_form,
|
||||||
)
|
)
|
||||||
make("manager.role.deletion", user=agent, session=session2, role=role_user)
|
make('manager.role.deletion', user=agent, session=session2, role=role_user)
|
||||||
make(
|
make(
|
||||||
"manager.role.membership.grant",
|
'manager.role.membership.grant',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
role=role_user,
|
role=role_user,
|
||||||
member=user,
|
member=user,
|
||||||
)
|
)
|
||||||
make(
|
make(
|
||||||
"manager.role.membership.removal",
|
'manager.role.membership.removal',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
role=role_user,
|
role=role_user,
|
||||||
|
@ -204,14 +204,14 @@ def events(db, superuser, freezer):
|
||||||
)
|
)
|
||||||
|
|
||||||
make(
|
make(
|
||||||
"manager.role.inheritance.addition",
|
'manager.role.inheritance.addition',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
parent=role_agent,
|
parent=role_agent,
|
||||||
child=role_user,
|
child=role_user,
|
||||||
)
|
)
|
||||||
make(
|
make(
|
||||||
"manager.role.inheritance.removal",
|
'manager.role.inheritance.removal',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
parent=role_agent,
|
parent=role_agent,
|
||||||
|
@ -219,14 +219,14 @@ def events(db, superuser, freezer):
|
||||||
)
|
)
|
||||||
|
|
||||||
make(
|
make(
|
||||||
"manager.role.administrator.role.addition",
|
'manager.role.administrator.role.addition',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
role=role_user,
|
role=role_user,
|
||||||
admin_role=role_agent,
|
admin_role=role_agent,
|
||||||
)
|
)
|
||||||
make(
|
make(
|
||||||
"manager.role.administrator.role.removal",
|
'manager.role.administrator.role.removal',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
role=role_user,
|
role=role_user,
|
||||||
|
@ -234,14 +234,14 @@ def events(db, superuser, freezer):
|
||||||
)
|
)
|
||||||
|
|
||||||
make(
|
make(
|
||||||
"manager.role.administrator.user.addition",
|
'manager.role.administrator.user.addition',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
role=role_user,
|
role=role_user,
|
||||||
admin_user=user,
|
admin_user=user,
|
||||||
)
|
)
|
||||||
make(
|
make(
|
||||||
"manager.role.administrator.user.removal",
|
'manager.role.administrator.user.removal',
|
||||||
user=agent,
|
user=agent,
|
||||||
session=session2,
|
session=session2,
|
||||||
role=role_user,
|
role=role_user,
|
||||||
|
@ -290,8 +290,8 @@ def events(db, superuser, freezer):
|
||||||
reason='ldap-reactivation',
|
reason='ldap-reactivation',
|
||||||
)
|
)
|
||||||
|
|
||||||
make("user.service.sso.refusal", user=user, session=session1, service=service)
|
make('user.service.sso.refusal', user=user, session=session1, service=service)
|
||||||
make("user.service.sso.denial", user=user, session=session1, service=service)
|
make('user.service.sso.denial', user=user, session=session1, service=service)
|
||||||
|
|
||||||
make(
|
make(
|
||||||
'user.profile.add',
|
'user.profile.add',
|
||||||
|
@ -341,11 +341,11 @@ def events(db, superuser, freezer):
|
||||||
make('user.notification.activity', actor=service, target_user=user)
|
make('user.notification.activity', actor=service, target_user=user)
|
||||||
make('user.notification.activity', actor=superuser, target_user=user)
|
make('user.notification.activity', actor=superuser, target_user=user)
|
||||||
|
|
||||||
make("user.password.reset", user=deleted_user)
|
make('user.password.reset', user=deleted_user)
|
||||||
deleted_user.delete()
|
deleted_user.delete()
|
||||||
|
|
||||||
# verify we created at least one event for each type
|
# verify we created at least one event for each type
|
||||||
assert set(Event.objects.values_list("type__name", flat=True)) == set(_registry)
|
assert set(Event.objects.values_list('type__name', flat=True)) == set(_registry)
|
||||||
|
|
||||||
return locals()
|
return locals()
|
||||||
|
|
||||||
|
@ -354,23 +354,23 @@ def extract_journal(response):
|
||||||
rows = []
|
rows = []
|
||||||
seen_event_ids = set()
|
seen_event_ids = set()
|
||||||
while True:
|
while True:
|
||||||
for tr in response.pyquery("tr[data-event-type]"):
|
for tr in response.pyquery('tr[data-event-type]'):
|
||||||
# page can overlap when they contain less than 20 items (to prevent orphan rows)
|
# page can overlap when they contain less than 20 items (to prevent orphan rows)
|
||||||
event_id = tr.attrib["data-event-id"]
|
event_id = tr.attrib['data-event-id']
|
||||||
if event_id not in seen_event_ids:
|
if event_id not in seen_event_ids:
|
||||||
rows.append(response.pyquery(tr))
|
rows.append(response.pyquery(tr))
|
||||||
seen_event_ids.add(event_id)
|
seen_event_ids.add(event_id)
|
||||||
if "Previous page" not in response:
|
if 'Previous page' not in response:
|
||||||
break
|
break
|
||||||
response = response.click("Previous page", index=0)
|
response = response.click('Previous page', index=0)
|
||||||
|
|
||||||
rows.reverse()
|
rows.reverse()
|
||||||
content = [
|
content = [
|
||||||
{
|
{
|
||||||
'timestamp': text_content(row.find(".journal-list--timestamp-column")[0]).strip(),
|
'timestamp': text_content(row.find('.journal-list--timestamp-column')[0]).strip(),
|
||||||
'type': row[0].attrib["data-event-type"],
|
'type': row[0].attrib['data-event-type'],
|
||||||
'user': text_content(row.find(".journal-list--user-column")[0]).strip(),
|
'user': text_content(row.find('.journal-list--user-column')[0]).strip(),
|
||||||
'message': text_content(row.find(".journal-list--message-column")[0]),
|
'message': text_content(row.find('.journal-list--message-column')[0]),
|
||||||
}
|
}
|
||||||
for row in rows
|
for row in rows
|
||||||
]
|
]
|
||||||
|
@ -378,7 +378,7 @@ def extract_journal(response):
|
||||||
|
|
||||||
|
|
||||||
def test_global_journal(app, superuser, events):
|
def test_global_journal(app, superuser, events):
|
||||||
response = login(app, user=superuser, path="/manage/")
|
response = login(app, user=superuser, path='/manage/')
|
||||||
set_attribute_action = SetAttributeAction.objects.get()
|
set_attribute_action = SetAttributeAction.objects.get()
|
||||||
|
|
||||||
# remove event about admin login
|
# remove event about admin login
|
||||||
|
@ -387,7 +387,7 @@ def test_global_journal(app, superuser, events):
|
||||||
# get deleted user
|
# get deleted user
|
||||||
deleted_user = DeletedUser.objects.all().first()
|
deleted_user = DeletedUser.objects.all().first()
|
||||||
|
|
||||||
response = response.click("Global journal")
|
response = response.click('Global journal')
|
||||||
|
|
||||||
content = extract_journal(response)
|
content = extract_journal(response)
|
||||||
|
|
||||||
|
@ -801,10 +801,10 @@ def test_global_journal(app, superuser, events):
|
||||||
|
|
||||||
|
|
||||||
def test_user_journal(app, superuser, events):
|
def test_user_journal(app, superuser, events):
|
||||||
response = login(app, user=superuser, path="/manage/")
|
response = login(app, user=superuser, path='/manage/')
|
||||||
user = User.objects.get(username="user")
|
user = User.objects.get(username='user')
|
||||||
|
|
||||||
response = app.get("/manage/users/%s/journal/" % user.id)
|
response = app.get('/manage/users/%s/journal/' % user.id)
|
||||||
content = extract_journal(response)
|
content = extract_journal(response)
|
||||||
|
|
||||||
assert content == [
|
assert content == [
|
||||||
|
@ -1054,11 +1054,11 @@ def test_user_journal(app, superuser, events):
|
||||||
|
|
||||||
|
|
||||||
def test_role_journal(app, superuser, events):
|
def test_role_journal(app, superuser, events):
|
||||||
response = login(app, user=superuser, path="/manage/")
|
response = login(app, user=superuser, path='/manage/')
|
||||||
role1 = Role.objects.get(name="role1")
|
role1 = Role.objects.get(name='role1')
|
||||||
role2 = Role.objects.get(name="role2")
|
role2 = Role.objects.get(name='role2')
|
||||||
|
|
||||||
response = app.get("/manage/roles/%s/journal/" % role1.id)
|
response = app.get('/manage/roles/%s/journal/' % role1.id)
|
||||||
content = extract_journal(response)
|
content = extract_journal(response)
|
||||||
|
|
||||||
assert content == [
|
assert content == [
|
||||||
|
@ -1130,7 +1130,7 @@ def test_role_journal(app, superuser, events):
|
||||||
},
|
},
|
||||||
]
|
]
|
||||||
|
|
||||||
response = app.get("/manage/roles/%s/journal/" % role2.id)
|
response = app.get('/manage/roles/%s/journal/' % role2.id)
|
||||||
content = extract_journal(response)
|
content = extract_journal(response)
|
||||||
|
|
||||||
assert content == [
|
assert content == [
|
||||||
|
@ -1239,7 +1239,7 @@ def test_roles_journal(app, superuser, events):
|
||||||
|
|
||||||
|
|
||||||
def test_date_navigation(app, superuser, events):
|
def test_date_navigation(app, superuser, events):
|
||||||
response = login(app, user=superuser, path="/manage/journal/")
|
response = login(app, user=superuser, path='/manage/journal/')
|
||||||
response = response.click('2020')
|
response = response.click('2020')
|
||||||
assert not response.context['form'].errors
|
assert not response.context['form'].errors
|
||||||
|
|
||||||
|
@ -1255,7 +1255,7 @@ def test_date_navigation(app, superuser, events):
|
||||||
|
|
||||||
|
|
||||||
def test_search(app, superuser, events):
|
def test_search(app, superuser, events):
|
||||||
response = login(app, user=superuser, path="/manage/journal/")
|
response = login(app, user=superuser, path='/manage/journal/')
|
||||||
response.form.set('search', 'event:registration')
|
response.form.set('search', 'event:registration')
|
||||||
response = response.form.submit()
|
response = response.form.submit()
|
||||||
assert len(response.pyquery('tbody tr')) == 2
|
assert len(response.pyquery('tbody tr')) == 2
|
||||||
|
@ -1376,7 +1376,7 @@ def test_search(app, superuser, events):
|
||||||
|
|
||||||
|
|
||||||
def test_search_empty(app, superuser, events):
|
def test_search_empty(app, superuser, events):
|
||||||
response = login(app, user=superuser, path="/manage/journal/")
|
response = login(app, user=superuser, path='/manage/journal/')
|
||||||
response.form.set('search', 'abcd123')
|
response.form.set('search', 'abcd123')
|
||||||
response = response.form.submit()
|
response = response.form.submit()
|
||||||
assert 'No event found.' in response.text
|
assert 'No event found.' in response.text
|
||||||
|
@ -1389,7 +1389,7 @@ def test_search_empty(app, superuser, events):
|
||||||
def test_delete_user(app, superuser, events):
|
def test_delete_user(app, superuser, events):
|
||||||
old_user_id = events['user'].id
|
old_user_id = events['user'].id
|
||||||
events['user'].delete()
|
events['user'].delete()
|
||||||
response = login(app, user=superuser, path="/manage/journal/")
|
response = login(app, user=superuser, path='/manage/journal/')
|
||||||
response.form.set('search', events['user'].email)
|
response.form.set('search', events['user'].email)
|
||||||
response = response.form.submit()
|
response = response.form.submit()
|
||||||
assert len(response.pyquery('tbody tr')) == 20
|
assert len(response.pyquery('tbody tr')) == 20
|
||||||
|
@ -1397,7 +1397,7 @@ def test_delete_user(app, superuser, events):
|
||||||
|
|
||||||
|
|
||||||
def test_event_type_list(app, superuser, events):
|
def test_event_type_list(app, superuser, events):
|
||||||
response = login(app, user=superuser, path="/manage/journal/")
|
response = login(app, user=superuser, path='/manage/journal/')
|
||||||
response = response.click('View available event types')
|
response = response.click('View available event types')
|
||||||
|
|
||||||
for e in Event.objects.all():
|
for e in Event.objects.all():
|
||||||
|
@ -1406,7 +1406,7 @@ def test_event_type_list(app, superuser, events):
|
||||||
|
|
||||||
def test_delete_authenticator(app, superuser, events):
|
def test_delete_authenticator(app, superuser, events):
|
||||||
events['authenticator'].delete()
|
events['authenticator'].delete()
|
||||||
response = login(app, superuser, path="/manage/journal/")
|
response = login(app, superuser, path='/manage/journal/')
|
||||||
response.form.set('search', 'event:authenticator.creation')
|
response.form.set('search', 'event:authenticator.creation')
|
||||||
response = response.form.submit()
|
response = response.form.submit()
|
||||||
assert len(response.pyquery('tbody tr')) == 1
|
assert len(response.pyquery('tbody tr')) == 1
|
||||||
|
|
|
@ -72,16 +72,16 @@ def test_manager_ou_import(app, admin, ou1, role_ou1, ou2, role_ou2):
|
||||||
# in case roles are present in export file, they must not be imported
|
# in case roles are present in export file, they must not be imported
|
||||||
export['roles'] = [
|
export['roles'] = [
|
||||||
{
|
{
|
||||||
"uuid": "27255f404cb140df9a577da76b59f285",
|
'uuid': '27255f404cb140df9a577da76b59f285',
|
||||||
"slug": "should_not_exist",
|
'slug': 'should_not_exist',
|
||||||
"name": "should_not_exist",
|
'name': 'should_not_exist',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
resp = resp.click('Import')
|
resp = resp.click('Import')
|
||||||
resp.form['site_json'] = Upload('export.json', json.dumps(export).encode(), 'application/json')
|
resp.form['site_json'] = Upload('export.json', json.dumps(export).encode(), 'application/json')
|
||||||
resp = resp.form.submit().follow()
|
resp = resp.form.submit().follow()
|
||||||
|
|
||||||
assert not Role.objects.filter(slug="should_not_exist").exists()
|
assert not Role.objects.filter(slug='should_not_exist').exists()
|
||||||
|
|
||||||
|
|
||||||
def test_ou_edit_form_local_options_overriden(app, admin, ou1, settings):
|
def test_ou_edit_form_local_options_overriden(app, admin, ou1, settings):
|
||||||
|
@ -92,13 +92,13 @@ def test_ou_edit_form_local_options_overriden(app, admin, ou1, settings):
|
||||||
|
|
||||||
response = app.get('/manage/organizational-units/%s/edit/' % ou1.pk)
|
response = app.get('/manage/organizational-units/%s/edit/' % ou1.pk)
|
||||||
|
|
||||||
assert 'disabled' not in response.pyquery("input#id_email_is_unique")[0].attrib
|
assert 'disabled' not in response.pyquery('input#id_email_is_unique')[0].attrib
|
||||||
assert response.pyquery("input#id_username_is_unique")[0].attrib['disabled']
|
assert response.pyquery('input#id_username_is_unique')[0].attrib['disabled']
|
||||||
|
|
||||||
settings.A2_EMAIL_IS_UNIQUE = True
|
settings.A2_EMAIL_IS_UNIQUE = True
|
||||||
settings.A2_USERNAME_IS_UNIQUE = False
|
settings.A2_USERNAME_IS_UNIQUE = False
|
||||||
|
|
||||||
response = app.get('/manage/organizational-units/%s/edit/' % ou1.pk)
|
response = app.get('/manage/organizational-units/%s/edit/' % ou1.pk)
|
||||||
|
|
||||||
assert response.pyquery("input#id_email_is_unique")[0].attrib['disabled']
|
assert response.pyquery('input#id_email_is_unique')[0].attrib['disabled']
|
||||||
assert 'disabled' not in response.pyquery("input#id_username_is_unique")[0].attrib
|
assert 'disabled' not in response.pyquery('input#id_username_is_unique')[0].attrib
|
||||||
|
|
|
@ -95,8 +95,8 @@ def test_send_password_reset_by_sms_code(app, nomail_user, settings, phone_activ
|
||||||
assert body['message'].startswith('Your code is')
|
assert body['message'].startswith('Your code is')
|
||||||
code = SMSCode.objects.get()
|
code = SMSCode.objects.get()
|
||||||
assert body['message'][-code_length:] == code.value
|
assert body['message'][-code_length:] == code.value
|
||||||
assert ("Your code is valid for the next %s minute" % (SMSCode.CODE_DURATION // 60)) in resp.text
|
assert ('Your code is valid for the next %s minute' % (SMSCode.CODE_DURATION // 60)) in resp.text
|
||||||
assert "The code you received by SMS." in resp.text
|
assert 'The code you received by SMS.' in resp.text
|
||||||
resp.form.set('sms_code', code.value)
|
resp.form.set('sms_code', code.value)
|
||||||
resp = resp.form.submit().follow()
|
resp = resp.form.submit().follow()
|
||||||
assert Token.objects.count() == 1
|
assert Token.objects.count() == 1
|
||||||
|
@ -199,7 +199,7 @@ def test_send_password_reset_by_sms_code_next_url(app, nomail_user, settings, ph
|
||||||
assert user == nomail_user
|
assert user == nomail_user
|
||||||
assert resp.location == '/accounts/consents/'
|
assert resp.location == '/accounts/consents/'
|
||||||
resp = resp.follow()
|
resp = resp.follow()
|
||||||
assert "Consent Management" in resp
|
assert 'Consent Management' in resp
|
||||||
|
|
||||||
|
|
||||||
def test_password_reset_empty_form(app, db, settings, phone_activated_authn):
|
def test_password_reset_empty_form(app, db, settings, phone_activated_authn):
|
||||||
|
|
|
@ -637,7 +637,7 @@ def test_registration_activate_passwords_not_equal(app, db, settings, mailoutbox
|
||||||
response.form.set('password1', 'azerty12AZ')
|
response.form.set('password1', 'azerty12AZ')
|
||||||
response.form.set('password2', 'AAAazerty12AZ')
|
response.form.set('password2', 'AAAazerty12AZ')
|
||||||
response = response.form.submit()
|
response = response.form.submit()
|
||||||
assert "The two password fields didn" in response.text
|
assert 'The two password fields didn' in response.text
|
||||||
|
|
||||||
|
|
||||||
def test_authentication_method(app, db, rf, hooks):
|
def test_authentication_method(app, db, rf, hooks):
|
||||||
|
@ -680,13 +680,13 @@ def test_authentication_method(app, db, rf, hooks):
|
||||||
def test_registration_with_email_suggestions(app, db, settings):
|
def test_registration_with_email_suggestions(app, db, settings):
|
||||||
url = utils_misc.make_url('registration_register')
|
url = utils_misc.make_url('registration_register')
|
||||||
response = app.get(url)
|
response = app.get(url)
|
||||||
assert "email_domains_suggestions.js" in response.text
|
assert 'email_domains_suggestions.js' in response.text
|
||||||
assert "field-live-hint" in response.text
|
assert 'field-live-hint' in response.text
|
||||||
|
|
||||||
settings.A2_SUGGESTED_EMAIL_DOMAINS = []
|
settings.A2_SUGGESTED_EMAIL_DOMAINS = []
|
||||||
response = app.get(url)
|
response = app.get(url)
|
||||||
assert "email_domains_suggestions.js" not in response.text
|
assert 'email_domains_suggestions.js' not in response.text
|
||||||
assert "field-live-hint" not in response.text
|
assert 'field-live-hint' not in response.text
|
||||||
|
|
||||||
|
|
||||||
def test_registration_no_email_full_profile_no_password(app, db, rf, mailoutbox):
|
def test_registration_no_email_full_profile_no_password(app, db, rf, mailoutbox):
|
||||||
|
@ -960,7 +960,7 @@ def test_registration_erroneous_phone_identifier(app, db, settings, phone_activa
|
||||||
resp.form.set('phone_1', 'thatsnotquiteit')
|
resp.form.set('phone_1', 'thatsnotquiteit')
|
||||||
resp = resp.form.submit()
|
resp = resp.form.submit()
|
||||||
assert (
|
assert (
|
||||||
"Phone number must be either in E.164 globally unique format or dialable from 33 country code (FR)."
|
'Phone number must be either in E.164 globally unique format or dialable from 33 country code (FR).'
|
||||||
in resp.pyquery('.error')[0].text_content()
|
in resp.pyquery('.error')[0].text_content()
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -1029,7 +1029,7 @@ def test_phone_registration_improperly_configured(app, db, settings, freezer, ca
|
||||||
assert not Token.objects.count()
|
assert not Token.objects.count()
|
||||||
assert not SMSCode.objects.count()
|
assert not SMSCode.objects.count()
|
||||||
assert (
|
assert (
|
||||||
"Something went wrong while trying to send the SMS code to you"
|
'Something went wrong while trying to send the SMS code to you'
|
||||||
in resp.pyquery('li.warning')[0].text_content()
|
in resp.pyquery('li.warning')[0].text_content()
|
||||||
)
|
)
|
||||||
assert caplog.records[0].message == 'settings.SMS_URL is not set'
|
assert caplog.records[0].message == 'settings.SMS_URL is not set'
|
||||||
|
@ -1049,7 +1049,7 @@ def test_phone_registration_connection_error(app, db, settings, freezer, caplog,
|
||||||
mock_send.return_value = mock_response
|
mock_send.return_value = mock_response
|
||||||
resp = resp.form.submit().follow().maybe_follow()
|
resp = resp.form.submit().follow().maybe_follow()
|
||||||
assert (
|
assert (
|
||||||
"Something went wrong while trying to send the SMS code to you"
|
'Something went wrong while trying to send the SMS code to you'
|
||||||
in resp.pyquery('li.warning')[0].text_content()
|
in resp.pyquery('li.warning')[0].text_content()
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
|
@ -1072,8 +1072,8 @@ def test_phone_registration(app, db, settings, phone_activated_authn):
|
||||||
assert body['message'].startswith('Your code is')
|
assert body['message'].startswith('Your code is')
|
||||||
code = SMSCode.objects.get()
|
code = SMSCode.objects.get()
|
||||||
assert body['message'][-code_length:] == code.value
|
assert body['message'][-code_length:] == code.value
|
||||||
assert ("Your code is valid for the next %s minute" % (SMSCode.CODE_DURATION // 60)) in resp.text
|
assert ('Your code is valid for the next %s minute' % (SMSCode.CODE_DURATION // 60)) in resp.text
|
||||||
assert "The code you received by SMS." in resp.text
|
assert 'The code you received by SMS.' in resp.text
|
||||||
resp.form.set('sms_code', code.value)
|
resp.form.set('sms_code', code.value)
|
||||||
resp = resp.form.submit().follow()
|
resp = resp.form.submit().follow()
|
||||||
assert Token.objects.count() == 1
|
assert Token.objects.count() == 1
|
||||||
|
@ -1083,7 +1083,7 @@ def test_phone_registration(app, db, settings, phone_activated_authn):
|
||||||
resp.form.set('first_name', 'John')
|
resp.form.set('first_name', 'John')
|
||||||
resp.form.set('last_name', 'Doe')
|
resp.form.set('last_name', 'Doe')
|
||||||
resp = resp.form.submit().follow()
|
resp = resp.form.submit().follow()
|
||||||
assert "You have just created an account" in resp.text
|
assert 'You have just created an account' in resp.text
|
||||||
|
|
||||||
user = User.objects.get(first_name='John', last_name='Doe')
|
user = User.objects.get(first_name='John', last_name='Doe')
|
||||||
assert user.attributes.phone == '+33612345678'
|
assert user.attributes.phone == '+33612345678'
|
||||||
|
@ -1115,7 +1115,7 @@ def test_phone_registration_nondefault_attribute(app, db, settings):
|
||||||
resp.form.set('first_name', 'John')
|
resp.form.set('first_name', 'John')
|
||||||
resp.form.set('last_name', 'Doe')
|
resp.form.set('last_name', 'Doe')
|
||||||
resp = resp.form.submit().follow()
|
resp = resp.form.submit().follow()
|
||||||
assert "You have just created an account" in resp.text
|
assert 'You have just created an account' in resp.text
|
||||||
|
|
||||||
user = User.objects.get(first_name='John', last_name='Doe')
|
user = User.objects.get(first_name='John', last_name='Doe')
|
||||||
assert user.attributes.another_phone == '+33612345678'
|
assert user.attributes.another_phone == '+33612345678'
|
||||||
|
|
|
@ -193,9 +193,9 @@ def test_manager_role_import(app, admin, ou1, role_ou1, ou2, role_ou2):
|
||||||
# in case ous are present in export file, they must not be imported
|
# in case ous are present in export file, they must not be imported
|
||||||
export['ous'] = [
|
export['ous'] = [
|
||||||
{
|
{
|
||||||
"uuid": "27255f404cb140df9a577da76b59f285",
|
'uuid': '27255f404cb140df9a577da76b59f285',
|
||||||
"slug": "should_not_exist",
|
'slug': 'should_not_exist',
|
||||||
"name": "should_not_exist",
|
'name': 'should_not_exist',
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
resp = app.get('/manage/roles/') # unselect ou1
|
resp = app.get('/manage/roles/') # unselect ou1
|
||||||
|
@ -204,10 +204,10 @@ def test_manager_role_import(app, admin, ou1, role_ou1, ou2, role_ou2):
|
||||||
resp.form['ou'] = get_default_ou().pk
|
resp.form['ou'] = get_default_ou().pk
|
||||||
resp = resp.form.submit().follow()
|
resp = resp.form.submit().follow()
|
||||||
|
|
||||||
assert not OrganizationalUnit.objects.filter(slug="should_not_exist").exists()
|
assert not OrganizationalUnit.objects.filter(slug='should_not_exist').exists()
|
||||||
|
|
||||||
# missing ou in export file
|
# missing ou in export file
|
||||||
export = {"roles": [{"slug": "agent", "name": "Agent"}]}
|
export = {'roles': [{'slug': 'agent', 'name': 'Agent'}]}
|
||||||
resp = app.get('/manage/roles/')
|
resp = app.get('/manage/roles/')
|
||||||
resp = resp.click('Import')
|
resp = resp.click('Import')
|
||||||
resp.form['site_json'] = Upload('export.json', json.dumps(export).encode(), 'application/json')
|
resp.form['site_json'] = Upload('export.json', json.dumps(export).encode(), 'application/json')
|
||||||
|
|
|
@ -721,9 +721,9 @@ def test_user_import_attributes(transactional_db, app, admin):
|
||||||
login(app, admin, '/manage/users/')
|
login(app, admin, '/manage/users/')
|
||||||
|
|
||||||
csv_lines = [
|
csv_lines = [
|
||||||
"email key verified,first_name,last_name,more,title,bike,saintsday,birthdate,zip,phone",
|
'email key verified,first_name,last_name,more,title,bike,saintsday,birthdate,zip,phone',
|
||||||
"elliot@universalpictures.com,Elliott,Thomas,petit,Mr,True,2019-7-20,1972-05-26,75014,0123456789",
|
'elliot@universalpictures.com,Elliott,Thomas,petit,Mr,True,2019-7-20,1972-05-26,75014,0123456789',
|
||||||
"et@universalpictures.com,ET,the Extra-Terrestrial,long,??,False,1/2/3/4,0002-2-22,42,home",
|
'et@universalpictures.com,ET,the Extra-Terrestrial,long,??,False,1/2/3/4,0002-2-22,42,home',
|
||||||
]
|
]
|
||||||
response = import_csv('\n'.join(csv_lines), app)
|
response = import_csv('\n'.join(csv_lines), app)
|
||||||
urls = re.findall('<a href="(/manage/users/import/[^/]+/[^/]+/)">', response.text)
|
urls = re.findall('<a href="(/manage/users/import/[^/]+/[^/]+/)">', response.text)
|
||||||
|
@ -743,7 +743,7 @@ def test_user_import_attributes(transactional_db, app, admin):
|
||||||
assert elliot.attributes.values['zip'].content == '75014'
|
assert elliot.attributes.values['zip'].content == '75014'
|
||||||
assert elliot.attributes.values['phone'].content == '+33123456789'
|
assert elliot.attributes.values['phone'].content == '+33123456789'
|
||||||
|
|
||||||
csv_lines[2] = "et@universalpictures.com,ET,the Extra-Terrestrial,,,,,,42000,+3281123456"
|
csv_lines[2] = 'et@universalpictures.com,ET,the Extra-Terrestrial,,,,,,42000,+3281123456'
|
||||||
response = import_csv('\n'.join(csv_lines), app)
|
response = import_csv('\n'.join(csv_lines), app)
|
||||||
assert '0 rows have errors' in response.text
|
assert '0 rows have errors' in response.text
|
||||||
|
|
||||||
|
@ -762,12 +762,12 @@ def test_detail_view(app, admin, simple_user, freezer, user_ou1, ou1):
|
||||||
url = f'/manage/users/{simple_user.pk}/'
|
url = f'/manage/users/{simple_user.pk}/'
|
||||||
resp = login(app, admin, url)
|
resp = login(app, admin, url)
|
||||||
assert str(simple_user.uuid) in resp.text
|
assert str(simple_user.uuid) in resp.text
|
||||||
assert "Last activity" not in resp.text
|
assert 'Last activity' not in resp.text
|
||||||
assert not resp.pyquery('.a2-manager-user-last-activity')
|
assert not resp.pyquery('.a2-manager-user-last-activity')
|
||||||
simple_user.keepalive = datetime.datetime(2023, 2, 1, 7)
|
simple_user.keepalive = datetime.datetime(2023, 2, 1, 7)
|
||||||
simple_user.save()
|
simple_user.save()
|
||||||
resp = app.get(url)
|
resp = app.get(url)
|
||||||
assert "Last activity on Feb. 1, 2023" in resp.pyquery('.a2-manager-user-last-activity')[0].text
|
assert 'Last activity on Feb. 1, 2023' in resp.pyquery('.a2-manager-user-last-activity')[0].text
|
||||||
logout(app)
|
logout(app)
|
||||||
|
|
||||||
ou1.clean_unused_accounts_alert = 700
|
ou1.clean_unused_accounts_alert = 700
|
||||||
|
@ -781,29 +781,29 @@ def test_detail_view(app, admin, simple_user, freezer, user_ou1, ou1):
|
||||||
freezer.move_to('2023-01-01')
|
freezer.move_to('2023-01-01')
|
||||||
resp = login(app, admin, url)
|
resp = login(app, admin, url)
|
||||||
assert (
|
assert (
|
||||||
"Deletion alert email planned for Dec. 1, 2024."
|
'Deletion alert email planned for Dec. 1, 2024.'
|
||||||
in resp.pyquery('.a2-manager-user-date-alert')[0].text
|
in resp.pyquery('.a2-manager-user-date-alert')[0].text
|
||||||
)
|
)
|
||||||
assert (
|
assert (
|
||||||
"Account deletion planned for Dec. 31, 2024."
|
'Account deletion planned for Dec. 31, 2024.'
|
||||||
in resp.pyquery('.a2-manager-user-date-deletion')[0].text
|
in resp.pyquery('.a2-manager-user-date-deletion')[0].text
|
||||||
)
|
)
|
||||||
logout(app)
|
logout(app)
|
||||||
|
|
||||||
freezer.move_to('2024-12-10')
|
freezer.move_to('2024-12-10')
|
||||||
resp = login(app, admin, url)
|
resp = login(app, admin, url)
|
||||||
assert "Deletion alert email sent on Dec. 1, 2024." in resp.pyquery('.a2-manager-user-date-alert')[0].text
|
assert 'Deletion alert email sent on Dec. 1, 2024.' in resp.pyquery('.a2-manager-user-date-alert')[0].text
|
||||||
assert (
|
assert (
|
||||||
"Account deletion planned for Dec. 31, 2024."
|
'Account deletion planned for Dec. 31, 2024.'
|
||||||
in resp.pyquery('.a2-manager-user-date-deletion')[0].text
|
in resp.pyquery('.a2-manager-user-date-deletion')[0].text
|
||||||
)
|
)
|
||||||
logout(app)
|
logout(app)
|
||||||
|
|
||||||
freezer.move_to('2025-01-01')
|
freezer.move_to('2025-01-01')
|
||||||
resp = login(app, admin, url)
|
resp = login(app, admin, url)
|
||||||
assert "Deletion alert email sent on Dec. 1, 2024." in resp.pyquery('.a2-manager-user-date-alert')[0].text
|
assert 'Deletion alert email sent on Dec. 1, 2024.' in resp.pyquery('.a2-manager-user-date-alert')[0].text
|
||||||
assert (
|
assert (
|
||||||
"Account deletion pending (should have been performed on Dec. 31, 2024)."
|
'Account deletion pending (should have been performed on Dec. 31, 2024).'
|
||||||
in resp.pyquery('.a2-manager-user-date-deletion')[0].text
|
in resp.pyquery('.a2-manager-user-date-deletion')[0].text
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -197,7 +197,7 @@ class TestDeleteAccountEmailVerified:
|
||||||
assert len(mailoutbox) == 2
|
assert len(mailoutbox) == 2
|
||||||
assert mailoutbox[1].subject == 'Account deletion on testserver'
|
assert mailoutbox[1].subject == 'Account deletion on testserver'
|
||||||
assert mailoutbox[0].to == [simple_user.email]
|
assert mailoutbox[0].to == [simple_user.email]
|
||||||
assert "Set-Cookie: messages=" in str(response) # Deletion performed
|
assert 'Set-Cookie: messages=' in str(response) # Deletion performed
|
||||||
assert urlparse(response.location).path == '/'
|
assert urlparse(response.location).path == '/'
|
||||||
|
|
||||||
def test_account_delete_when_logged_out(self, app, simple_user, mailoutbox):
|
def test_account_delete_when_logged_out(self, app, simple_user, mailoutbox):
|
||||||
|
@ -220,7 +220,7 @@ class TestDeleteAccountEmailVerified:
|
||||||
assert len(mailoutbox) == 2
|
assert len(mailoutbox) == 2
|
||||||
assert mailoutbox[1].subject == 'Account deletion on testserver'
|
assert mailoutbox[1].subject == 'Account deletion on testserver'
|
||||||
assert mailoutbox[0].to == [simple_user.email]
|
assert mailoutbox[0].to == [simple_user.email]
|
||||||
assert "Set-Cookie: messages=" in str(response) # Deletion performed
|
assert 'Set-Cookie: messages=' in str(response) # Deletion performed
|
||||||
assert urlparse(response.location).path == '/'
|
assert urlparse(response.location).path == '/'
|
||||||
|
|
||||||
def test_account_delete_by_other_user(self, app, simple_user, user_ou1, mailoutbox):
|
def test_account_delete_by_other_user(self, app, simple_user, user_ou1, mailoutbox):
|
||||||
|
@ -246,7 +246,7 @@ class TestDeleteAccountEmailVerified:
|
||||||
assert len(mailoutbox) == 2
|
assert len(mailoutbox) == 2
|
||||||
assert mailoutbox[1].subject == 'Account deletion on testserver'
|
assert mailoutbox[1].subject == 'Account deletion on testserver'
|
||||||
assert mailoutbox[0].to == [simple_user.email]
|
assert mailoutbox[0].to == [simple_user.email]
|
||||||
assert "Set-Cookie: messages=" in str(response) # Deletion performed
|
assert 'Set-Cookie: messages=' in str(response) # Deletion performed
|
||||||
assert urlparse(response.location).path == '/'
|
assert urlparse(response.location).path == '/'
|
||||||
|
|
||||||
def test_account_delete_fake_token(self, app, simple_user, mailoutbox):
|
def test_account_delete_fake_token(self, app, simple_user, mailoutbox):
|
||||||
|
@ -255,7 +255,7 @@ class TestDeleteAccountEmailVerified:
|
||||||
.follow()
|
.follow()
|
||||||
.follow()
|
.follow()
|
||||||
)
|
)
|
||||||
assert "The account deletion request is invalid, try again" in response.text
|
assert 'The account deletion request is invalid, try again' in response.text
|
||||||
|
|
||||||
def test_account_delete_expired_token(self, app, simple_user, mailoutbox, freezer):
|
def test_account_delete_expired_token(self, app, simple_user, mailoutbox, freezer):
|
||||||
freezer.move_to('2019-08-01')
|
freezer.move_to('2019-08-01')
|
||||||
|
@ -264,7 +264,7 @@ class TestDeleteAccountEmailVerified:
|
||||||
freezer.move_to('2019-08-04') # Too late...
|
freezer.move_to('2019-08-04') # Too late...
|
||||||
link = get_link_from_mail(mailoutbox[0])
|
link = get_link_from_mail(mailoutbox[0])
|
||||||
response = app.get(link).follow()
|
response = app.get(link).follow()
|
||||||
assert "The account deletion request is too old, try again" in response.text
|
assert 'The account deletion request is too old, try again' in response.text
|
||||||
|
|
||||||
def test_account_delete_valid_token_unexistent_user(self, app, simple_user, mailoutbox):
|
def test_account_delete_valid_token_unexistent_user(self, app, simple_user, mailoutbox):
|
||||||
page = login(app, simple_user, path=reverse('delete_account'))
|
page = login(app, simple_user, path=reverse('delete_account'))
|
||||||
|
@ -296,7 +296,7 @@ class TestDeleteAccountEmailNotVerified:
|
||||||
assert len(mailoutbox) == 1
|
assert len(mailoutbox) == 1
|
||||||
assert mailoutbox[0].subject == 'Account deletion on testserver'
|
assert mailoutbox[0].subject == 'Account deletion on testserver'
|
||||||
assert mailoutbox[0].to == [simple_user.email]
|
assert mailoutbox[0].to == [simple_user.email]
|
||||||
assert "Set-Cookie: messages=" in str(response) # Deletion performed
|
assert 'Set-Cookie: messages=' in str(response) # Deletion performed
|
||||||
assert urlparse(response.location).path == '/'
|
assert urlparse(response.location).path == '/'
|
||||||
|
|
||||||
def test_account_delete_old_authentication(self, app, simple_user, mailoutbox, freezer):
|
def test_account_delete_old_authentication(self, app, simple_user, mailoutbox, freezer):
|
||||||
|
@ -316,7 +316,7 @@ class TestDeleteAccountEmailNotVerified:
|
||||||
assert len(mailoutbox) == 1
|
assert len(mailoutbox) == 1
|
||||||
assert mailoutbox[0].subject == 'Account deletion on testserver'
|
assert mailoutbox[0].subject == 'Account deletion on testserver'
|
||||||
assert mailoutbox[0].to == [simple_user.email]
|
assert mailoutbox[0].to == [simple_user.email]
|
||||||
assert "Set-Cookie: messages=" in str(response) # Deletion performed
|
assert 'Set-Cookie: messages=' in str(response) # Deletion performed
|
||||||
assert urlparse(response.location).path == '/'
|
assert urlparse(response.location).path == '/'
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -168,7 +168,7 @@ def assert_equals_url(url1, url2, **kwargs):
|
||||||
elt2[k] = elt1.get(k, v)
|
elt2[k] = elt1.get(k, v)
|
||||||
else:
|
else:
|
||||||
elt2[k] = set(v)
|
elt2[k] = set(v)
|
||||||
assert elt1 == elt2, "URLs are not equal: %s != %s" % (splitted1, splitted2)
|
assert elt1 == elt2, 'URLs are not equal: %s != %s' % (splitted1, splitted2)
|
||||||
|
|
||||||
|
|
||||||
def assert_redirects_complex(response, expected_url, **kwargs):
|
def assert_redirects_complex(response, expected_url, **kwargs):
|
||||||
|
|
Loading…
Reference in New Issue