diff --git a/docs/developer/django-rest-framework-utils.rst b/docs/developer/django-rest-framework-utils.rst index b37153646..985d7a5a1 100644 --- a/docs/developer/django-rest-framework-utils.rst +++ b/docs/developer/django-rest-framework-utils.rst @@ -323,18 +323,14 @@ Usage example: class FloorPlanOrganizationFilter(FilterDjangoByOrgManaged): - organization_slug = filters.CharFilter( - field_name="organization__slug" - ) + organization_slug = filters.CharFilter(field_name="organization__slug") class Meta: model = FloorPlan fields = ["organization", "organization_slug"] - class FloorPlanListCreateView( - ProtectedAPIMixin, generics.ListCreateAPIView - ): + class FloorPlanListCreateView(ProtectedAPIMixin, generics.ListCreateAPIView): serializer_class = FloorPlanSerializer queryset = FloorPlan.objects.select_related().order_by("-created") pagination_class = ListViewPagination @@ -360,9 +356,7 @@ Usage example: model = FloorPlan - class FloorPlanListCreateView( - ProtectedAPIMixin, generics.ListCreateAPIView - ): + class FloorPlanListCreateView(ProtectedAPIMixin, generics.ListCreateAPIView): serializer_class = FloorPlanSerializer queryset = FloorPlan.objects.select_related().order_by("-created") pagination_class = ListViewPagination diff --git a/docs/developer/extending.rst b/docs/developer/extending.rst index 67cdad26c..a2968cc8a 100644 --- a/docs/developer/extending.rst +++ b/docs/developer/extending.rst @@ -176,9 +176,7 @@ Once you have created the models, add the following to your # but users are free to implement it in their projects if needed # for more information refer to the django-organizations docs: # https://django-organizations.readthedocs.io/ - OPENWISP_USERS_ORGANIZATIONINVITATION_MODEL = ( - "myusers.OrganizationInvitation" - ) + OPENWISP_USERS_ORGANIZATIONINVITATION_MODEL = "myusers.OrganizationInvitation" Substitute ``myusers`` with the name you chose in step 1. diff --git a/openwisp_users/__init__.py b/openwisp_users/__init__.py index c8e813a38..54283c5d1 100644 --- a/openwisp_users/__init__.py +++ b/openwisp_users/__init__.py @@ -1,14 +1,14 @@ -VERSION = (1, 2, 0, 'alpha') +VERSION = (1, 2, 0, "alpha") __version__ = VERSION # alias def get_version(): - version = '%s.%s.%s' % (VERSION[0], VERSION[1], VERSION[2]) - if VERSION[3] != 'final': + version = "%s.%s.%s" % (VERSION[0], VERSION[1], VERSION[2]) + if VERSION[3] != "final": first_letter = VERSION[3][0:1] try: suffix = VERSION[4] except IndexError: suffix = 0 - version = '%s%s%s' % (version, first_letter, suffix) + version = "%s%s%s" % (version, first_letter, suffix) return version diff --git a/openwisp_users/accounts/adapter.py b/openwisp_users/accounts/adapter.py index befdb6d56..4d2eb7b46 100644 --- a/openwisp_users/accounts/adapter.py +++ b/openwisp_users/accounts/adapter.py @@ -13,21 +13,21 @@ def send_mail(self, template_prefix, email, context): subject = self.format_email_subject(subject) content = {} errors = {} - for ext in ['html', 'txt']: - template_name = '{0}_message.{1}'.format(template_prefix, ext) - if 'activate_url' in context: - context['call_to_action_url'] = context['activate_url'] - context['call_to_action_text'] = _('Confirm') + for ext in ["html", "txt"]: + template_name = "{0}_message.{1}".format(template_prefix, ext) + if "activate_url" in context: + context["call_to_action_url"] = context["activate_url"] + context["call_to_action_text"] = _("Confirm") try: - template_name = '{0}_message.{1}'.format(template_prefix, ext) + template_name = "{0}_message.{1}".format(template_prefix, ext) content[ext] = render_to_string( template_name, context, self.request ).strip() except TemplateDoesNotExist as e: errors[ext] = e - text = content.get('txt', '') - html = content.get('html', '') + text = content.get("txt", "") + html = content.get("html", "") # both templates fail to load, raise the exception if len(errors.keys()) >= 2: - raise errors['txt'] from errors['html'] + raise errors["txt"] from errors["html"] send_email(subject, text, html, [email], context) diff --git a/openwisp_users/accounts/urls.py b/openwisp_users/accounts/urls.py index 9ae8c3bf0..eb4157546 100644 --- a/openwisp_users/accounts/urls.py +++ b/openwisp_users/accounts/urls.py @@ -16,73 +16,73 @@ from .views import password_change, password_change_success -redirect_view = RedirectView.as_view(url=reverse_lazy('admin:index')) +redirect_view = RedirectView.as_view(url=reverse_lazy("admin:index")) urlpatterns = [ - path('signup/', redirect_view, name='account_signup'), - path('login/', views.login, name='account_login'), - path('logout/', views.logout, name='account_logout'), - path('inactive/', views.account_inactive, name='account_inactive'), + path("signup/", redirect_view, name="account_signup"), + path("login/", views.login, name="account_login"), + path("logout/", views.logout, name="account_logout"), + path("inactive/", views.account_inactive, name="account_inactive"), # E-mail path( - 'confirm-email/', + "confirm-email/", views.email_verification_sent, - name='account_email_verification_sent', + name="account_email_verification_sent", ), re_path( - r'^confirm-email/(?P[-:\w]+)/$', + r"^confirm-email/(?P[-:\w]+)/$", views.confirm_email, - name='account_confirm_email', + name="account_confirm_email", ), # password change path( - 'password/change/', + "password/change/", password_change, name="account_change_password", ), path( - 'password/change/success/', + "password/change/success/", password_change_success, - name='account_change_password_success', + name="account_change_password_success", ), # password reset - path('password/reset/', views.password_reset, name='account_reset_password'), + path("password/reset/", views.password_reset, name="account_reset_password"), path( - 'password/reset/done/', + "password/reset/done/", views.password_reset_done, - name='account_reset_password_done', + name="account_reset_password_done", ), re_path( - r'^password/reset/key/(?P[0-9A-Za-z]+)-(?P.+)/$', + r"^password/reset/key/(?P[0-9A-Za-z]+)-(?P.+)/$", views.password_reset_from_key, - name='account_reset_password_from_key', + name="account_reset_password_from_key", ), path( - 'password/reset/key/done/', + "password/reset/key/done/", views.password_reset_from_key_done, - name='account_reset_password_from_key_done', + name="account_reset_password_from_key_done", ), path( - 'email-verification-success/', - TemplateView.as_view(template_name='account/email_verification_success.html'), - name='email_confirmation_success', + "email-verification-success/", + TemplateView.as_view(template_name="account/email_verification_success.html"), + name="email_confirmation_success", ), path( - 'logout-success/', - TemplateView.as_view(template_name='account/logout_success.html'), - name='logout_success', + "logout-success/", + TemplateView.as_view(template_name="account/logout_success.html"), + name="logout_success", ), ] if app_settings.SOCIALACCOUNT_ENABLED: - urlpatterns += [path('social/', include('allauth.socialaccount.urls'))] + urlpatterns += [path("social/", include("allauth.socialaccount.urls"))] for provider in providers.registry.get_class_list(): try: - prov_mod = import_module(provider.get_package() + '.urls') + prov_mod = import_module(provider.get_package() + ".urls") except ImportError: continue - prov_urlpatterns = getattr(prov_mod, 'urlpatterns', None) + prov_urlpatterns = getattr(prov_mod, "urlpatterns", None) if prov_urlpatterns: urlpatterns += prov_urlpatterns diff --git a/openwisp_users/accounts/views.py b/openwisp_users/accounts/views.py index d0362a734..060f84dba 100644 --- a/openwisp_users/accounts/views.py +++ b/openwisp_users/accounts/views.py @@ -15,8 +15,8 @@ class ChangePasswordForm(BaseChangePasswordForm): class PasswordChangeView(BasePasswordChangeView): form_class = ChangePasswordForm - template_name = 'account/password_change.html' - success_url = reverse_lazy('account_change_password_success') + template_name = "account/password_change.html" + success_url = reverse_lazy("account_change_password_success") def get_success_url(self): if self.request.POST.get(REDIRECT_FIELD_NAME): @@ -25,17 +25,17 @@ def get_success_url(self): def get_initial(self): data = super().get_initial() - data['next'] = self.request.GET.get(REDIRECT_FIELD_NAME) + data["next"] = self.request.GET.get(REDIRECT_FIELD_NAME) return data @sensitive_post_parameters_m def dispatch(self, request, *args, **kwargs): if not self.request.user.has_usable_password(): - return render(self.request, 'account/password_not_required.html') + return render(self.request, "account/password_not_required.html") return super().dispatch(request, *args, **kwargs) password_change = login_required(PasswordChangeView.as_view()) password_change_success = login_required( - TemplateView.as_view(template_name='account/password_change_success.html') + TemplateView.as_view(template_name="account/password_change_success.html") ) diff --git a/openwisp_users/admin.py b/openwisp_users/admin.py index 405749701..b8a946bc2 100644 --- a/openwisp_users/admin.py +++ b/openwisp_users/admin.py @@ -36,10 +36,10 @@ from .multitenancy import MultitenantAdminMixin, MultitenantOrgFilter from .utils import BaseAdmin -Group = load_model('openwisp_users', 'Group') -Organization = load_model('openwisp_users', 'Organization') -OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner') -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') +Group = load_model("openwisp_users", "Group") +Organization = load_model("openwisp_users", "Organization") +OrganizationOwner = load_model("openwisp_users", "OrganizationOwner") +OrganizationUser = load_model("openwisp_users", "OrganizationUser") User = get_user_model() logger = logging.getLogger(__name__) @@ -47,7 +47,7 @@ class EmailAddressInline(admin.StackedInline): model = EmailAddress extra = 0 - readonly_fields = ['email'] + readonly_fields = ["email"] def has_add_permission(self, *args, **kwargs): """ @@ -84,7 +84,7 @@ def _construct_form(self, i, **kwargs): class OrganizationOwnerInline(admin.StackedInline): model = OrganizationOwner extra = 0 - autocomplete_fields = ('organization_user',) + autocomplete_fields = ("organization_user",) def has_change_permission(self, request, obj=None): if obj and not request.user.is_superuser and not request.user.is_owner(obj): @@ -96,7 +96,7 @@ class OrganizationUserInline(admin.StackedInline): model = OrganizationUser formset = RequiredInlineFormSet view_on_site = False - autocomplete_fields = ('organization',) + autocomplete_fields = ("organization",) def get_formset(self, request, obj=None, **kwargs): """ @@ -108,10 +108,8 @@ def get_formset(self, request, obj=None, **kwargs): if request.user.is_superuser: return formset if not request.user.is_superuser: - formset.form.base_fields[ - 'organization' - ].queryset = Organization.objects.filter( - pk__in=request.user.organizations_managed + formset.form.base_fields["organization"].queryset = ( + Organization.objects.filter(pk__in=request.user.organizations_managed) ) return formset @@ -126,7 +124,7 @@ class OrganizationUserInlineReadOnly(OrganizationUserInline): def get_readonly_fields(self, request, obj=None): if obj and not request.user.is_superuser: - self.readonly_fields = ['is_admin'] + self.readonly_fields = ["is_admin"] return self.readonly_fields def has_add_permission(self, request, obj=None): @@ -139,15 +137,15 @@ def has_change_permission(self, request, obj=None): class UserFormMixin(forms.ModelForm): - email = forms.EmailField(label=_('Email'), max_length=254, required=True) + email = forms.EmailField(label=_("Email"), max_length=254, required=True) def validate_user_groups(self, data): - is_staff = data.get('is_staff') - is_superuser = data.get('is_superuser') - groups = data.get('groups') + is_staff = data.get("is_staff") + is_superuser = data.get("is_superuser") + groups = data.get("groups") if is_staff and not is_superuser and not groups: raise ValidationError( - {'groups': _('A staff user must belong to a group, please select one.')} + {"groups": _("A staff user must belong to a group, please select one.")} ) def clean(self): @@ -160,30 +158,30 @@ class UserCreationForm(UserFormMixin, BaseUserCreationForm): phone_number = PhoneNumberField(widget=forms.TextInput(), required=False) class Meta(BaseUserCreationForm.Meta): - fields = ['username', 'email', 'password1', 'password2'] - personal_fields = ['first_name', 'last_name', 'phone_number', 'birth_date'] + fields = ["username", "email", "password1", "password2"] + personal_fields = ["first_name", "last_name", "phone_number", "birth_date"] fieldsets = ( - (None, {'classes': ('wide',), 'fields': fields}), - ('Personal Info', {'classes': ('wide',), 'fields': personal_fields}), + (None, {"classes": ("wide",), "fields": fields}), + ("Personal Info", {"classes": ("wide",), "fields": personal_fields}), ( - 'Permissions', - {'classes': ('wide',), 'fields': ['is_active', 'is_staff', 'groups']}, + "Permissions", + {"classes": ("wide",), "fields": ["is_active", "is_staff", "groups"]}, ), ) fieldsets_superuser = ( - (None, {'classes': ('wide',), 'fields': fields}), - ('Personal Info', {'classes': ('wide',), 'fields': personal_fields}), + (None, {"classes": ("wide",), "fields": fields}), + ("Personal Info", {"classes": ("wide",), "fields": personal_fields}), ( - 'Permissions', + "Permissions", { - 'classes': ('wide',), - 'fields': ['is_active', 'is_staff', 'is_superuser', 'groups'], + "classes": ("wide",), + "fields": ["is_active", "is_staff", "is_superuser", "groups"], }, ), ) class Media: - js = ('admin/js/jquery.init.js', 'openwisp-users/js/addform.js') + js = ("admin/js/jquery.init.js", "openwisp-users/js/addform.js") class UserChangeForm(UserFormMixin, BaseUserChangeForm): @@ -193,24 +191,24 @@ class UserChangeForm(UserFormMixin, BaseUserChangeForm): class UserAdmin(MultitenantAdminMixin, BaseUserAdmin, BaseAdmin): add_form = UserCreationForm form = UserChangeForm - ordering = ['-date_joined'] - readonly_fields = ['last_login', 'date_joined', 'password_updated'] + ordering = ["-date_joined"] + readonly_fields = ["last_login", "date_joined", "password_updated"] list_display = [ - 'username', - 'email', - 'is_active', - 'is_staff', - 'is_superuser', - 'date_joined', - 'last_login', + "username", + "email", + "is_active", + "is_staff", + "is_superuser", + "date_joined", + "last_login", ] inlines = [EmailAddressInline, OrganizationUserInline] save_on_top = True - actions = ['delete_selected_overridden', 'make_inactive', 'make_active'] + actions = ["delete_selected_overridden", "make_inactive", "make_active"] fieldsets = list(BaseUserAdmin.fieldsets) # To ensure extended apps use this template. - change_form_template = 'admin/openwisp_users/user/change_form.html' + change_form_template = "admin/openwisp_users/user/change_form.html" def require_confirmation(func): """ @@ -223,17 +221,17 @@ def require_confirmation(func): def wrapper(modeladmin, request, queryset): opts = modeladmin.model._meta - if request.POST.get('confirmation') is None: + if request.POST.get("confirmation") is None: request.current_app = modeladmin.admin_site.name context = { **modeladmin.admin_site.each_context(request), - 'title': _('Are you sure?'), - 'action': request.POST['action'], - 'queryset': queryset, - 'opts': opts, + "title": _("Are you sure?"), + "action": request.POST["action"], + "queryset": queryset, + "opts": opts, } return TemplateResponse( - request, 'admin/action_confirmation.html', context + request, "admin/action_confirmation.html", context ) return func(modeladmin, request, queryset) @@ -241,7 +239,7 @@ def wrapper(modeladmin, request, queryset): return wrapper @admin.action( - description=_('Flag selected users as inactive'), permissions=['change'] + description=_("Flag selected users as inactive"), permissions=["change"] ) @require_confirmation def make_inactive(self, request, queryset): @@ -251,14 +249,14 @@ def make_inactive(self, request, queryset): self.message_user( request, _( - f'Successfully made {count} ' - f'{model_ngettext(self.opts, count)} inactive.' + f"Successfully made {count} " + f"{model_ngettext(self.opts, count)} inactive." ), messages.SUCCESS, ) @admin.action( - description=_('Flag selected users as active'), permissions=['change'] + description=_("Flag selected users as active"), permissions=["change"] ) @require_confirmation def make_active(self, request, queryset): @@ -268,8 +266,8 @@ def make_active(self, request, queryset): self.message_user( request, _( - f'Successfully made {count} ' - f'{model_ngettext(self.opts, count)} active.' + f"Successfully made {count} " + f"{model_ngettext(self.opts, count)} active." ), messages.SUCCESS, ) @@ -279,19 +277,19 @@ def get_list_display(self, request): Hide `is_superuser` from column from operators """ default_list_display = super().get_list_display(request) - if not request.user.is_superuser and 'is_superuser' in default_list_display: + if not request.user.is_superuser and "is_superuser" in default_list_display: # avoid editing the default_list_display operators_list_display = default_list_display[:] - operators_list_display.remove('is_superuser') + operators_list_display.remove("is_superuser") return operators_list_display return default_list_display def get_list_filter(self, request): filters = super().get_list_filter(request) - if not request.user.is_superuser and 'is_superuser' in filters: + if not request.user.is_superuser and "is_superuser" in filters: # hide is_superuser filter for non-superusers operators_filter_list = list(filters) - operators_filter_list.remove('is_superuser') + operators_filter_list.remove("is_superuser") return tuple(operators_filter_list) return filters @@ -307,10 +305,10 @@ def get_fieldsets(self, request, obj=None): if not request.user.is_superuser: # edit this tuple to add / remove permission items # visible to non-superusers - user_permissions = ('is_active', 'is_staff', 'groups', 'user_permissions') + user_permissions = ("is_active", "is_staff", "groups", "user_permissions") # copy to avoid modifying reference non_superuser_fieldsets = deepcopy(fieldsets) - non_superuser_fieldsets[2][1]['fields'] = user_permissions + non_superuser_fieldsets[2][1]["fields"] = user_permissions return non_superuser_fieldsets return fieldsets @@ -320,7 +318,7 @@ def get_readonly_fields(self, request, obj=None): # do not allow operators to escalate their privileges if not request.user.is_superuser: # copy to avoid modifying reference - fields = fields[:] + ['user_permissions', 'is_superuser'] + fields = fields[:] + ["user_permissions", "is_superuser"] return fields def has_change_permission(self, request, obj=None): @@ -341,20 +339,20 @@ def has_delete_permission(self, request, obj=None): def get_actions(self, request): actions = super().get_actions(request) - if not request.POST.get('post') and 'delete_selected' in actions: - del actions['delete_selected'] + if not request.POST.get("post") and "delete_selected" in actions: + del actions["delete_selected"] return actions - @admin.action(description=delete_selected.short_description, permissions=['delete']) + @admin.action(description=delete_selected.short_description, permissions=["delete"]) def delete_selected_overridden(self, request, queryset): if not request.user.is_superuser: - users_pk = queryset.values_list('pk', flat=True) + users_pk = queryset.values_list("pk", flat=True) owners_list = list( OrganizationOwner.objects.filter(organization_user__user__in=users_pk) - .select_related('organization_user__user') - .values_list('organization_user__user__username', flat=True) + .select_related("organization_user__user") + .values_list("organization_user__user__username", flat=True) ) - owners = ', '.join(owners_list) + owners = ", ".join(owners_list) excluded_owners_qs = queryset.exclude(username__in=owners_list) # if trying to delete any owner, show an error message count = len(owners_list) @@ -372,7 +370,7 @@ def delete_selected_overridden(self, request, queryset): # if trying to delete only owners, stop here if queryset.exists() and not excluded_owners_qs.exists(): redirect_url = reverse( - f'admin:{self.model._meta.app_label}_user_changelist' + f"admin:{self.model._meta.app_label}_user_changelist" ) return HttpResponseRedirect(redirect_url) # otherwise proceed but remove owners from the delete queryset @@ -400,7 +398,7 @@ def get_inline_instances(self, request, obj=None): return inlines inline = OrganizationUserInline(self.model, self.admin_site) if request: - if hasattr(inline, '_has_add_permission'): + if hasattr(inline, "_has_add_permission"): has_add_perm = inline._has_add_permission(request, obj) else: has_add_perm = inline.has_add_permission(request, obj) @@ -408,12 +406,12 @@ def get_inline_instances(self, request, obj=None): return [inline] return [] - def change_view(self, request, object_id, form_url='', extra_context=None): + def change_view(self, request, object_id, form_url="", extra_context=None): extra_context = extra_context or {} obj = self.get_object(request, object_id) if obj is not None and user_not_allowed_to_change_owner(request.user, obj): show_owner_warning = True - extra_context.update({'show_owner_warning': show_owner_warning}) + extra_context.update({"show_owner_warning": show_owner_warning}) return super().change_view(request, object_id, form_url, extra_context) def save_model(self, request, obj, form, change): @@ -429,8 +427,8 @@ def save_model(self, request, obj, form, change): ) except Exception as e: logger.exception( - 'Got exception {} while sending ' - 'verification email to user {}, email {}'.format( + "Got exception {} while sending " + "verification email to user {}, email {}".format( type(e), obj.username, obj.email ) ) @@ -446,11 +444,11 @@ def save_formset(self, request, form, formset, change): if not_deleted: single_msg = ( f"Can't delete {not_deleted} organization user because it " - 'belongs to an organization owner.' + "belongs to an organization owner." ) multiple_msg = ( f"Can't delete {not_deleted} organization users because they " - 'belong to some organization owners.' + "belong to some organization owners." ) self.message_user( request, ngettext(single_msg, multiple_msg, not_deleted), messages.ERROR @@ -464,7 +462,7 @@ class OrganizationUserFilter(MultitenantOrgFilter): Allows filtering users by the organization they're related to """ - field_name = f'{Organization._meta.app_label}_organization' + field_name = f"{Organization._meta.app_label}_organization" def queryset(self, request, queryset): if self.value(): @@ -474,24 +472,24 @@ def queryset(self, request, queryset): return queryset -base_fields = list(UserAdmin.fieldsets[1][1]['fields']) -additional_fields = ['bio', 'url', 'company', 'location', 'phone_number', 'birth_date'] -UserAdmin.fieldsets[1][1]['fields'] = base_fields + additional_fields -UserAdmin.fieldsets.insert(3, ('Internal', {'fields': ('notes',)})) -primary_fields = list(UserAdmin.fieldsets[0][1]['fields']) -UserAdmin.fieldsets[0][1]['fields'] = primary_fields + ['password_updated'] -UserAdmin.add_fieldsets[0][1]['fields'] = ( - 'username', - 'email', - 'password1', - 'password2', +base_fields = list(UserAdmin.fieldsets[1][1]["fields"]) +additional_fields = ["bio", "url", "company", "location", "phone_number", "birth_date"] +UserAdmin.fieldsets[1][1]["fields"] = base_fields + additional_fields +UserAdmin.fieldsets.insert(3, ("Internal", {"fields": ("notes",)})) +primary_fields = list(UserAdmin.fieldsets[0][1]["fields"]) +UserAdmin.fieldsets[0][1]["fields"] = primary_fields + ["password_updated"] +UserAdmin.add_fieldsets[0][1]["fields"] = ( + "username", + "email", + "password1", + "password2", ) -UserAdmin.search_fields += ('phone_number',) +UserAdmin.search_fields += ("phone_number",) UserAdmin.list_filter = (OrganizationUserFilter,) + UserAdmin.list_filter class GroupAdmin(BaseGroupAdmin, BaseAdmin): - if 'reversion' in settings.INSTALLED_APPS: + if "reversion" in settings.INSTALLED_APPS: # Correctly register the proxy model def reversion_register(self, model, **kwargs): return super().reversion_register(model, for_concrete_model=False, **kwargs) @@ -504,9 +502,9 @@ class OrganizationAdmin( # this inline has an autocomplete field pointing to OrganizationUserAdmin if app_settings.ORGANIZATION_USER_ADMIN and app_settings.ORGANIZATION_OWNER_ADMIN: inlines = [OrganizationOwnerInline] - readonly_fields = ['uuid', 'created', 'modified'] - ordering = ['name'] - list_display = ['name', 'is_active', 'created', 'modified'] + readonly_fields = ["uuid", "created", "modified"] + ordering = ["name"] + list_display = ["name", "is_active", "created", "modified"] def get_inline_instances(self, request, obj=None): """ @@ -529,15 +527,15 @@ def has_change_permission(self, request, obj=None): return super().has_change_permission(request, obj) class Media(UUIDAdmin.Media): - css = {'all': ('openwisp-users/css/admin.css',)} + css = {"all": ("openwisp-users/css/admin.css",)} class OrganizationUserAdmin( MultitenantAdminMixin, BaseOrganizationUserAdmin, BaseAdmin ): view_on_site = False - actions = ['delete_selected_overridden'] - search_fields = ['user__username', 'organization__name'] + actions = ["delete_selected_overridden"] + search_fields = ["user__username", "organization__name"] def get_readonly_fields(self, request, obj=None): # retrieve readonly fields @@ -545,7 +543,7 @@ def get_readonly_fields(self, request, obj=None): # do not allow operators to escalate their privileges if not request.user.is_superuser: # copy to avoid modifying reference - fields = ['is_admin'] + fields = ["is_admin"] return fields def has_delete_permission(self, request, obj=None): @@ -567,23 +565,23 @@ def delete_view(self, request, object_id, extra_context=None): request, _( "Can't delete this organization user because " - 'it belongs to an organization owner.' + "it belongs to an organization owner." ), messages.ERROR, ) redirect_url = reverse( - f'admin:{self.model._meta.app_label}_organizationuser_change', + f"admin:{self.model._meta.app_label}_organizationuser_change", args=[object_id], ) return HttpResponseRedirect(redirect_url) def get_actions(self, request): actions = super().get_actions(request) - if not request.POST.get('post') and 'delete_selected' in actions: - del actions['delete_selected'] + if not request.POST.get("post") and "delete_selected" in actions: + del actions["delete_selected"] return actions - @admin.action(description=delete_selected.short_description, permissions=['delete']) + @admin.action(description=delete_selected.short_description, permissions=["delete"]) def delete_selected_overridden(self, request, queryset): count = 0 pks = [] @@ -599,7 +597,7 @@ def delete_selected_overridden(self, request, queryset): messages.ERROR, ) redirect_url = reverse( - f'admin:{self.model._meta.app_label}_organizationuser_changelist' + f"admin:{self.model._meta.app_label}_organizationuser_changelist" ) return HttpResponseRedirect(redirect_url) # if some org owners' org users were selected @@ -607,11 +605,11 @@ def delete_selected_overridden(self, request, queryset): queryset = queryset.exclude(pk__in=pks) single_msg = ( f"Can't delete {count} organization user because it " - 'belongs to an organization owner.' + "belongs to an organization owner." ) multiple_msg = ( f"Can't delete {count} organization users because they " - 'belong to some organization owners.' + "belong to some organization owners." ) self.message_user( request, ngettext(single_msg, multiple_msg, count), messages.ERROR @@ -623,9 +621,9 @@ def delete_selected_overridden(self, request, queryset): class OrganizationOwnerAdmin( MultitenantAdminMixin, BaseOrganizationOwnerAdmin, BaseAdmin ): - list_display = ('get_user', 'organization') + list_display = ("get_user", "organization") if app_settings.ORGANIZATION_USER_ADMIN and app_settings.ORGANIZATION_OWNER_ADMIN: - autocomplete_fields = ['organization_user', 'organization'] + autocomplete_fields = ["organization_user", "organization"] def get_user(self, obj): return obj.organization_user.user @@ -642,29 +640,29 @@ def get_user(self, obj): admin.site.register(OrganizationOwner, OrganizationOwnerAdmin) # unregister auth.Group -base_group_model = apps.get_model('auth', 'Group') +base_group_model = apps.get_model("auth", "Group") admin.site.unregister(base_group_model) # register openwisp_users.Group proxy model admin.site.register(Group, GroupAdmin) # unregister some admin components to keep the admin interface simple # we can re-enable these models later when they will be really needed -EmailAddress = apps.get_model('account', 'EmailAddress') +EmailAddress = apps.get_model("account", "EmailAddress") if admin.site.is_registered(EmailAddress): admin.site.unregister(EmailAddress) if allauth_settings.SOCIALACCOUNT_ENABLED: for model in [ - ('socialaccount', 'SocialApp'), - ('socialaccount', 'SocialToken'), - ('socialaccount', 'SocialAccount'), + ("socialaccount", "SocialApp"), + ("socialaccount", "SocialToken"), + ("socialaccount", "SocialAccount"), ]: model_class = apps.get_model(*model) if admin.site.is_registered(model_class): admin.site.unregister(model_class) -if 'rest_framework.authtoken' in settings.INSTALLED_APPS: # pragma: no cover - TokenProxy = apps.get_model('authtoken', 'TokenProxy') +if "rest_framework.authtoken" in settings.INSTALLED_APPS: # pragma: no cover + TokenProxy = apps.get_model("authtoken", "TokenProxy") if admin.site.is_registered(TokenProxy): admin.site.unregister(TokenProxy) diff --git a/openwisp_users/api/authentication.py b/openwisp_users/api/authentication.py index 5afb08c36..470c0d404 100644 --- a/openwisp_users/api/authentication.py +++ b/openwisp_users/api/authentication.py @@ -11,7 +11,7 @@ class BearerAuthentication(TokenAuthentication): - keyword = 'Bearer' + keyword = "Bearer" class SesameAuthentication(BaseAuthentication): @@ -23,18 +23,18 @@ def authenticate(self, request): return None if len(auth) == 1: - msg = _('Invalid token header. No credentials provided.') + msg = _("Invalid token header. No credentials provided.") raise exceptions.AuthenticationFailed(msg) elif len(auth) > 2: - msg = _('Invalid token header. Token string should not contain spaces.') + msg = _("Invalid token header. Token string should not contain spaces.") raise exceptions.AuthenticationFailed(msg) try: token = auth[1].decode() except UnicodeError: msg = _( - 'Invalid token header. ' - 'Token string should not contain invalid characters.' + "Invalid token header. " + "Token string should not contain invalid characters." ) raise exceptions.AuthenticationFailed(msg) @@ -43,5 +43,5 @@ def authenticate(self, request): def authenticate_credentials(self, key): user = get_user_from_one_time_auth_token(key) if user is None: - raise exceptions.AuthenticationFailed(_('Invalid or expired token.')) + raise exceptions.AuthenticationFailed(_("Invalid or expired token.")) return (user, key) diff --git a/openwisp_users/api/filters.py b/openwisp_users/api/filters.py index c31d525e5..81aa5c742 100644 --- a/openwisp_users/api/filters.py +++ b/openwisp_users/api/filters.py @@ -13,7 +13,7 @@ class BaseOrganizationFilter: """ class Meta: - fields = ['organization', 'organization_slug'] + fields = ["organization", "organization_slug"] class OrganizationMembershipFilter(BaseOrganizationFilter, FilterDjangoByOrgMembership): @@ -22,7 +22,7 @@ class OrganizationMembershipFilter(BaseOrganizationFilter, FilterDjangoByOrgMemb to filter relation fields based on the user organization membership """ - organization_slug = filters.CharFilter(field_name='organization__slug') + organization_slug = filters.CharFilter(field_name="organization__slug") class OrganizationManagedFilter(BaseOrganizationFilter, FilterDjangoByOrgManaged): @@ -31,7 +31,7 @@ class OrganizationManagedFilter(BaseOrganizationFilter, FilterDjangoByOrgManaged to filter relation fields based on the organization managed by the user """ - organization_slug = filters.CharFilter(field_name='organization__slug') + organization_slug = filters.CharFilter(field_name="organization__slug") class OrganizationOwnedFilter(BaseOrganizationFilter, FilterDjangoByOrgOwned): @@ -40,4 +40,4 @@ class OrganizationOwnedFilter(BaseOrganizationFilter, FilterDjangoByOrgOwned): to filter relation fields based on the organization owned by the user """ - organization_slug = filters.CharFilter(field_name='organization__slug') + organization_slug = filters.CharFilter(field_name="organization__slug") diff --git a/openwisp_users/api/mixins.py b/openwisp_users/api/mixins.py index 9721f0237..0eb32756f 100644 --- a/openwisp_users/api/mixins.py +++ b/openwisp_users/api/mixins.py @@ -10,17 +10,17 @@ from .authentication import BearerAuthentication from .permissions import DjangoModelPermissions, IsOrganizationManager -Organization = swapper.load_model('openwisp_users', 'Organization') +Organization = swapper.load_model("openwisp_users", "Organization") class OrgLookup: @property def org_field(self): - return getattr(self, 'organization_field', 'organization') + return getattr(self, "organization_field", "organization") @property def organization_lookup(self): - return f'{self.org_field}__in' + return f"{self.org_field}__in" class SharedObjectsLookup: @@ -31,7 +31,7 @@ def queryset_organization_conditions(self): # If user has access to any organization, then include shared # objects in the queryset. if len(organizations): - conditions |= Q(**{f'{self.org_field}__isnull': True}) + conditions |= Q(**{f"{self.org_field}__isnull": True}) return conditions @@ -70,7 +70,7 @@ class FilterByOrganizationMembership(FilterByOrganization): Filter queryset by organizations the user is a member of """ - _user_attr = 'organizations_dict' + _user_attr = "organizations_dict" class FilterByOrganizationManaged(SharedObjectsLookup, FilterByOrganization): @@ -78,7 +78,7 @@ class FilterByOrganizationManaged(SharedObjectsLookup, FilterByOrganization): Filter queryset by organizations managed by user """ - _user_attr = 'organizations_managed' + _user_attr = "organizations_managed" class FilterByOrganizationOwned(SharedObjectsLookup, FilterByOrganization): @@ -86,7 +86,7 @@ class FilterByOrganizationOwned(SharedObjectsLookup, FilterByOrganization): Filter queryset by organizations owned by user """ - _user_attr = 'organizations_owned' + _user_attr = "organizations_owned" class FilterByParent(OrgLookup): @@ -127,7 +127,7 @@ class FilterByParentMembership(FilterByParent): Filter queryset based on parent organization membership """ - _user_attr = 'organizations_dict' + _user_attr = "organizations_dict" class FilterByParentManaged(FilterByParent): @@ -135,7 +135,7 @@ class FilterByParentManaged(FilterByParent): Filter queryset based on parent organizations managed by user """ - _user_attr = 'organizations_managed' + _user_attr = "organizations_managed" class FilterByParentOwned(FilterByParent): @@ -143,7 +143,7 @@ class FilterByParentOwned(FilterByParent): Filter queryset based on parent organizations owned by user """ - _user_attr = 'organizations_owned' + _user_attr = "organizations_owned" class FilterSerializerByOrganization(OrgLookup): @@ -158,14 +158,14 @@ def _user_attr(self): raise NotImplementedError() def filter_fields(self): - user = self.context['request'].user + user = self.context["request"].user # superuser can see everything if user.is_superuser or user.is_anonymous: return # non superusers can see only items of organizations they're related to organization_filter = getattr(user, self._user_attr) for field in self.fields: - if field == 'organization' and not self.fields[field].read_only: + if field == "organization" and not self.fields[field].read_only: # queryset attribute will not be present if set to read_only self.fields[field].allow_null = False self.fields[field].queryset = self.fields[field].queryset.filter( @@ -186,7 +186,7 @@ def __init__(self, *args, **kwargs): super().__init__(*args, **kwargs) # only filter related fields if the serializer # is being initiated during an HTTP request - if 'request' in self.context: + if "request" in self.context: self.filter_fields() @@ -195,7 +195,7 @@ class FilterSerializerByOrgMembership(FilterSerializerByOrganization): Filter serializer by organizations the user is member of """ - _user_attr = 'organizations_dict' + _user_attr = "organizations_dict" class FilterSerializerByOrgManaged(FilterSerializerByOrganization): @@ -203,7 +203,7 @@ class FilterSerializerByOrgManaged(FilterSerializerByOrganization): Filter serializer by organizations managed by user """ - _user_attr = 'organizations_managed' + _user_attr = "organizations_managed" class FilterSerializerByOrgOwned(FilterSerializerByOrganization): @@ -211,7 +211,7 @@ class FilterSerializerByOrgOwned(FilterSerializerByOrganization): Filter serializer by organizations owned by user """ - _user_attr = 'organizations_owned' + _user_attr = "organizations_owned" class QuerySetRequestMixin(BaseQuerySetRequestMixin): @@ -225,15 +225,15 @@ def get_queryset(self, request): # of organizations they're related to organization_filter = getattr(user, self._user_attr) # if field_name organization then just organization_filter - if self._filter_field == 'organization': + if self._filter_field == "organization": return queryset.filter(pk__in=organization_filter) # for field_name other than organization - conditions = Q(**{'organization__in': organization_filter}) + conditions = Q(**{"organization__in": organization_filter}) return queryset.filter(conditions) def __init__(self, *args, **kwargs): - self._user_attr = kwargs.pop('user_attr') - self._filter_field = kwargs.pop('filter_field') + self._user_attr = kwargs.pop("user_attr") + self._filter_field = kwargs.pop("filter_field") super().__init__(*args, **kwargs) @@ -254,9 +254,9 @@ class FilterDjangoOrganization(filters.FilterSet): """ @classmethod - def filter_for_field(cls, field, name, lookup_expr='exact'): + def filter_for_field(cls, field, name, lookup_expr="exact"): if isinstance(field, ForeignKey) or isinstance(field, ManyToManyField): - if field.name == 'user': + if field.name == "user": return super().filter_for_field(field, name, lookup_expr) opts = dict( queryset=field.remote_field.model.objects.all(), @@ -277,7 +277,7 @@ class FilterDjangoByOrgMembership(FilterDjangoOrganization): Filter django-filters by organizations the user is member of """ - _user_attr = 'organizations_dict' + _user_attr = "organizations_dict" class FilterDjangoByOrgManaged(FilterDjangoOrganization): @@ -285,7 +285,7 @@ class FilterDjangoByOrgManaged(FilterDjangoOrganization): Filter django-filters by organizations managed by user """ - _user_attr = 'organizations_managed' + _user_attr = "organizations_managed" class FilterDjangoByOrgOwned(FilterDjangoOrganization): @@ -293,7 +293,7 @@ class FilterDjangoByOrgOwned(FilterDjangoOrganization): Filter django-filters by organizations owned by user """ - _user_attr = 'organizations_owned' + _user_attr = "organizations_owned" class ProtectedAPIMixin(object): diff --git a/openwisp_users/api/permissions.py b/openwisp_users/api/permissions.py index e8ee60960..4177229f3 100644 --- a/openwisp_users/api/permissions.py +++ b/openwisp_users/api/permissions.py @@ -5,21 +5,21 @@ ) from swapper import load_model -Organization = load_model('openwisp_users', 'Organization') +Organization = load_model("openwisp_users", "Organization") class ObjectOrganizationMixin(object): def get_object_organization(self, view, obj): - organization_field = getattr(view, 'organization_field', 'organization') - fields = organization_field.split('__') + organization_field = getattr(view, "organization_field", "organization") + fields = organization_field.split("__") accessed_object = obj for field in fields: accessed_object = getattr(accessed_object, field, False) if accessed_object is False: raise AttributeError( _( - 'Organization not found, `organization_field` ' - 'not implemented correctly.' + "Organization not found, `organization_field` " + "not implemented correctly." ) ) return accessed_object @@ -46,17 +46,17 @@ def has_permission(self, request, view): def validate_membership(self, user, org): raise NotImplementedError( _( - 'View\'s permission_classes not implemented correctly.' - 'Please use one of the child classes: IsOrganizationMember, ' - 'IsOrganizationManager or IsOrganizationOwner.' + "View's permission_classes not implemented correctly." + "Please use one of the child classes: IsOrganizationMember, " + "IsOrganizationManager or IsOrganizationOwner." ) ) class IsOrganizationMember(BaseOrganizationPermission): message = _( - 'User is not a member of the organization to which the ' - 'requested resource belongs.' + "User is not a member of the organization to which the " + "requested resource belongs." ) def validate_membership(self, user, org): @@ -65,8 +65,8 @@ def validate_membership(self, user, org): class IsOrganizationManager(BaseOrganizationPermission): message = _( - 'User is not a manager of the organization to which the ' - 'requested resource belongs.' + "User is not a manager of the organization to which the " + "requested resource belongs." ) def has_permission(self, request, view): @@ -81,8 +81,8 @@ def validate_membership(self, user, org): class IsOrganizationOwner(BaseOrganizationPermission): message = _( - 'User is not a owner of the organization to which the ' - 'requested resource belongs.' + "User is not a owner of the organization to which the " + "requested resource belongs." ) def has_permission(self, request, view): @@ -97,20 +97,20 @@ def validate_membership(self, user, org): class DjangoModelPermissions(ObjectOrganizationMixin, BaseDjangoModelPermissions): perms_map = { - 'GET': ['%(app_label)s.view_%(model_name)s'], - 'OPTIONS': [], - 'HEAD': ['%(app_label)s.view_%(model_name)s'], - 'POST': ['%(app_label)s.add_%(model_name)s'], - 'PUT': ['%(app_label)s.change_%(model_name)s'], - 'PATCH': ['%(app_label)s.change_%(model_name)s'], - 'DELETE': ['%(app_label)s.delete_%(model_name)s'], + "GET": ["%(app_label)s.view_%(model_name)s"], + "OPTIONS": [], + "HEAD": ["%(app_label)s.view_%(model_name)s"], + "POST": ["%(app_label)s.add_%(model_name)s"], + "PUT": ["%(app_label)s.change_%(model_name)s"], + "PATCH": ["%(app_label)s.change_%(model_name)s"], + "DELETE": ["%(app_label)s.delete_%(model_name)s"], } - READ_ONLY_METHOD = ['GET', 'HEAD'] + READ_ONLY_METHOD = ["GET", "HEAD"] def has_permission(self, request, view): # Workaround to ensure DjangoModelPermissions are not applied # to the root view when using DefaultRouter. - if getattr(view, '_ignore_model_permissions', False): + if getattr(view, "_ignore_model_permissions", False): return True user = request.user @@ -119,9 +119,9 @@ def has_permission(self, request, view): queryset = self._queryset(view) perms = self.get_required_permissions(request.method, queryset.model) - change_perm = self.get_required_permissions('PUT', queryset.model) + change_perm = self.get_required_permissions("PUT", queryset.model) - if request.method == 'GET': + if request.method == "GET": return user.has_perms(perms) or user.has_perms(change_perm) return user.has_perms(perms) diff --git a/openwisp_users/api/serializers.py b/openwisp_users/api/serializers.py index c0659aaa3..bc19bc1bc 100644 --- a/openwisp_users/api/serializers.py +++ b/openwisp_users/api/serializers.py @@ -11,43 +11,43 @@ from openwisp_utils.api.serializers import ValidatedModelSerializer -Group = load_model('openwisp_users', 'Group') -Organization = load_model('openwisp_users', 'Organization') +Group = load_model("openwisp_users", "Group") +Organization = load_model("openwisp_users", "Organization") User = get_user_model() -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') +OrganizationUser = load_model("openwisp_users", "OrganizationUser") logger = logging.getLogger(__name__) -OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner') +OrganizationOwner = load_model("openwisp_users", "OrganizationOwner") class OrganizationSerializer(ValidatedModelSerializer): class Meta: model = Organization fields = ( - 'id', - 'name', - 'is_active', - 'slug', - 'description', - 'email', - 'url', - 'created', - 'modified', + "id", + "name", + "is_active", + "slug", + "description", + "email", + "url", + "created", + "modified", ) # This allows replacing the default "UniqueValidator" for # the slug field with the custom validation defined below. - extra_kwargs = {'slug': {'validators': []}} + extra_kwargs = {"slug": {"validators": []}} def validate(self, data): """ Custom validation error if an organization already exists with the given slug. """ - org = Organization.objects.filter(slug__exact=data.get('slug')).first() + org = Organization.objects.filter(slug__exact=data.get("slug")).first() if org: raise serializers.ValidationError( { - 'slug': _('organization with this slug already exists.'), - 'organization': org.pk, + "slug": _("organization with this slug already exists."), + "organization": org.pk, } ) return super().validate(data) @@ -58,7 +58,7 @@ def display_value(self, instance): return instance.user.username def get_queryset(self): - user = self.context['request'].user + user = self.context["request"].user if user.is_superuser: queryset = OrganizationUser.objects.all() else: @@ -73,8 +73,8 @@ class OrganizationOwnerSerializer(serializers.ModelSerializer): class Meta: model = OrganizationOwner - fields = ('organization_user',) - extra_kwargs = {'organization_user': {'allow_null': True}} + fields = ("organization_user",) + extra_kwargs = {"organization_user": {"allow_null": True}} class OrganizationDetailSerializer(serializers.ModelSerializer): @@ -83,28 +83,28 @@ class OrganizationDetailSerializer(serializers.ModelSerializer): class Meta: model = Organization fields = ( - 'id', - 'name', - 'is_active', - 'slug', - 'description', - 'email', - 'url', - 'owner', - 'created', - 'modified', + "id", + "name", + "is_active", + "slug", + "description", + "email", + "url", + "owner", + "created", + "modified", ) def update(self, instance, validated_data): - if validated_data.get('owner'): - org_owner = validated_data.pop('owner') + if validated_data.get("owner"): + org_owner = validated_data.pop("owner") existing_owner = OrganizationOwner.objects.filter(organization=instance) if ( existing_owner.exists() is False - and org_owner['organization_user'] is not None + and org_owner["organization_user"] is not None ): - org_user = org_owner.get('organization_user') + org_user = org_owner.get("organization_user") with transaction.atomic(): org_owner = OrganizationOwner.objects.create( organization=instance, organization_user=org_user @@ -114,13 +114,13 @@ def update(self, instance, validated_data): return super().update(instance, validated_data) if existing_owner.exists(): - if org_owner['organization_user'] is None: + if org_owner["organization_user"] is None: existing_owner.first().delete() return super().update(instance, validated_data) existing_owner_user = existing_owner[0].organization_user - if org_owner.get('organization_user') != existing_owner_user: - org_user = org_owner.get('organization_user') + if org_owner.get("organization_user") != existing_owner_user: + org_user = org_owner.get("organization_user") with transaction.atomic(): existing_owner.first().delete() org_owner = OrganizationOwner.objects.create( @@ -136,27 +136,27 @@ def update(self, instance, validated_data): class MyPrimaryKeyRelatedField(serializers.PrimaryKeyRelatedField): def to_representation(self, value): - return f'{value.pk}: {value.natural_key()[2]} | {value.name}' + return f"{value.pk}: {value.natural_key()[2]} | {value.name}" def to_internal_value(self, value): if type(value) is int: return value - return int(value.partition(':')[0]) + return int(value.partition(":")[0]) class GroupSerializer(serializers.ModelSerializer): permissions = MyPrimaryKeyRelatedField( many=True, - queryset=Permission.objects.select_related('content_type'), + queryset=Permission.objects.select_related("content_type"), required=False, ) class Meta: model = Group - fields = ('id', 'name', 'permissions') + fields = ("id", "name", "permissions") def create(self, validated_data): - permissions = validated_data.pop('permissions') + permissions = validated_data.pop("permissions") instance = self.instance or self.Meta.model(**validated_data) instance.full_clean() instance.save() @@ -164,8 +164,8 @@ def create(self, validated_data): return instance def update(self, instance, validated_data): - if 'permissions' in validated_data: - permissions = validated_data.pop('permissions') + if "permissions" in validated_data: + permissions = validated_data.pop("permissions") instance.permissions.clear() instance.permissions.add(*permissions) instance.full_clean() @@ -174,7 +174,7 @@ def update(self, instance, validated_data): class OrgUserCustomPrimarykeyRelatedField(serializers.PrimaryKeyRelatedField): def get_queryset(self): - user = self.context['request'].user + user = self.context["request"].user if user.is_superuser: queryset = Organization.objects.all() else: @@ -188,8 +188,8 @@ class OrganizationUserSerializer(serializers.ModelSerializer): class Meta: model = OrganizationUser fields = ( - 'is_admin', - 'organization', + "is_admin", + "organization", ) def to_internal_value(self, data): @@ -210,10 +210,10 @@ def to_representation(self, instance): list_of_org_users = [] for org_user in org_users: user = dict() - user['is_admin'] = org_user.is_admin - user['organization'] = org_user.organization.id + user["is_admin"] = org_user.is_admin + user["organization"] = org_user.organization.id list_of_org_users.append(user) - data['organization_users'] = list_of_org_users + data["organization_users"] = list_of_org_users return data @@ -223,37 +223,37 @@ class SuperUserListSerializer(BaseSuperUserSerializer): class Meta: model = User fields = ( - 'id', - 'username', - 'email', - 'email_verified', - 'password', - 'first_name', - 'last_name', - 'phone_number', - 'birth_date', - 'is_active', - 'is_staff', - 'is_superuser', - 'groups', - 'organization_users', + "id", + "username", + "email", + "email_verified", + "password", + "first_name", + "last_name", + "phone_number", + "birth_date", + "is_active", + "is_staff", + "is_superuser", + "groups", + "organization_users", ) - read_only_fields = ('last_login', 'date_joined') + read_only_fields = ("last_login", "date_joined") extra_kwargs = { - 'email': {'required': True}, - 'password': {'write_only': True, 'style': {'input_type': 'password'}}, + "email": {"required": True}, + "password": {"write_only": True, "style": {"input_type": "password"}}, } def validate_email(self, value): if not value: - raise serializers.ValidationError(_('This field cannot be blank.')) + raise serializers.ValidationError(_("This field cannot be blank.")) return value def create(self, validated_data): - group_data = validated_data.pop('groups', None) - org_user_data = validated_data.pop('organization_users', None) - password = validated_data.pop('password') - email_verified = validated_data.pop('email_verified', False) + group_data = validated_data.pop("groups", None) + org_user_data = validated_data.pop("organization_users", None) + password = validated_data.pop("password") + email_verified = validated_data.pop("email_verified", False) instance = self.instance or self.Meta.model(**validated_data) instance.set_password(password) @@ -264,8 +264,8 @@ def create(self, validated_data): instance.groups.add(*group_data) if org_user_data: - if org_user_data.get('organization') is not None: - org_user_data['user'] = instance + if org_user_data.get("organization") is not None: + org_user_data["user"] = instance org_user_instance = OrganizationUser(**org_user_data) org_user_instance.full_clean() org_user_instance.save() @@ -273,7 +273,7 @@ def create(self, validated_data): if instance.email: try: email = EmailAddress.objects.add_email( - self.context['request'], + self.context["request"], user=instance, email=instance.email, confirm=not email_verified, @@ -282,11 +282,11 @@ def create(self, validated_data): if email_verified: email.primary = True email.verified = True - email.save(update_fields=['primary', 'verified']) + email.save(update_fields=["primary", "verified"]) except Exception as e: logger.exception( - 'Got exception {} while sending ' - 'verification email to user {}, email {}'.format( + "Got exception {} while sending " + "verification email to user {}, email {}".format( type(e), instance.username, instance.email ) ) @@ -298,58 +298,58 @@ class SuperUserDetailSerializer(BaseSuperUserSerializer): class Meta: model = User fields = ( - 'id', - 'username', - 'first_name', - 'last_name', - 'email', - 'bio', - 'url', - 'company', - 'location', - 'phone_number', - 'birth_date', - 'notes', - 'is_active', - 'is_staff', - 'is_superuser', - 'last_login', - 'date_joined', - 'groups', - 'user_permissions', - 'organization_users', + "id", + "username", + "first_name", + "last_name", + "email", + "bio", + "url", + "company", + "location", + "phone_number", + "birth_date", + "notes", + "is_active", + "is_staff", + "is_superuser", + "last_login", + "date_joined", + "groups", + "user_permissions", + "organization_users", ) extra_kwargs = { - 'last_login': {'read_only': True}, - 'date_joined': {'read_only': True}, + "last_login": {"read_only": True}, + "date_joined": {"read_only": True}, } def update(self, instance, validated_data): org_user_data = dict() - if validated_data.get('organization_users'): - org_user_data = validated_data.pop('organization_users') + if validated_data.get("organization_users"): + org_user_data = validated_data.pop("organization_users") - if org_user_data.get('organization') is not None: + if org_user_data.get("organization") is not None: org_user = None try: org_user = OrganizationUser.objects.get( - user=instance, organization=org_user_data['organization'] + user=instance, organization=org_user_data["organization"] ) except OrganizationUser.DoesNotExist: pass if org_user: if ( - str(org_user_data['organization'].id) + str(org_user_data["organization"].id) in instance.organizations_dict.keys() ): - if org_user.is_admin != org_user_data.get('is_admin'): - org_user.is_admin = org_user_data['is_admin'] + if org_user.is_admin != org_user_data.get("is_admin"): + org_user.is_admin = org_user_data["is_admin"] org_user.full_clean() org_user.save() else: org_user.delete() else: - org_user_data['user'] = instance + org_user_data["user"] = instance org_user_instance = OrganizationUser(**org_user_data) org_user_instance.full_clean() org_user_instance.save() @@ -364,7 +364,7 @@ def get_userlist_fields(fields): Returns the fields for `UserListSerializer`. """ fields = list(fields) - fields.remove('is_superuser') + fields.remove("is_superuser") fields = tuple(fields) return fields @@ -373,10 +373,10 @@ class UserListSerializer(SuperUserListSerializer): class Meta: model = User fields = get_userlist_fields(SuperUserListSerializer.Meta.fields[:]) - read_only_fields = ('last_login', 'date_joined') + read_only_fields = ("last_login", "date_joined") extra_kwargs = { - 'email': {'required': True}, - 'password': {'write_only': True, 'style': {'input_type': 'password'}}, + "email": {"required": True}, + "password": {"write_only": True, "style": {"input_type": "password"}}, } @@ -385,8 +385,8 @@ def get_userdetail_fields(fields): Returns the fields for `UserDetailSerializer`. """ fields = list(fields) - fields.remove('is_superuser') - fields.remove('user_permissions') + fields.remove("is_superuser") + fields.remove("user_permissions") fields = tuple(fields) return fields @@ -398,61 +398,61 @@ class Meta: model = User fields = get_userdetail_fields(SuperUserDetailSerializer.Meta.fields[:]) extra_kwargs = { - 'last_login': {'read_only': True}, - 'date_joined': {'read_only': True}, + "last_login": {"read_only": True}, + "date_joined": {"read_only": True}, } class ChangePasswordSerializer(serializers.Serializer): current_password = serializers.CharField( - allow_null=True, write_only=True, style={'input_type': 'password'} + allow_null=True, write_only=True, style={"input_type": "password"} ) new_password = serializers.CharField( - required=True, write_only=True, style={'input_type': 'password'} + required=True, write_only=True, style={"input_type": "password"} ) confirm_password = serializers.CharField( - required=True, write_only=True, style={'input_type': 'password'} + required=True, write_only=True, style={"input_type": "password"} ) def validate_confirm_password(self, value): - if self.initial_data.get('new_password') != self.initial_data.get( - 'confirm_password' + if self.initial_data.get("new_password") != self.initial_data.get( + "confirm_password" ): raise serializers.ValidationError( - _('The two password fields didn’t match.') + _("The two password fields didn’t match.") ) return value def validate_current_password(self, value): - logged_user = self.context['request'].user + logged_user = self.context["request"].user if logged_user.is_superuser: return value elif logged_user.organizations_managed: return value else: - if self.initial_data.get('new_password'): - to_change_user = self.context['user'] + if self.initial_data.get("new_password"): + to_change_user = self.context["user"] if not to_change_user.check_password(value): raise serializers.ValidationError( _( - 'Your old password was entered incorrectly.' - ' Please enter it again.' + "Your old password was entered incorrectly." + " Please enter it again." ) ) return value def validate_new_password(self, value): - if self.initial_data.get('current_password') == self.initial_data.get( - 'new_password' + if self.initial_data.get("current_password") == self.initial_data.get( + "new_password" ): raise serializers.ValidationError( - _('New password cannot be the same as your old password.') + _("New password cannot be the same as your old password.") ) return value def save(self, **kwargs): - password = self.validated_data['new_password'] - user = self.context['user'] + password = self.validated_data["new_password"] + user = self.context["user"] user.set_password(password) user.save() @@ -460,11 +460,9 @@ def save(self, **kwargs): class EmailAddressSerializer(ValidatedModelSerializer): class Meta: model = EmailAddress - fields = ('id', 'email', 'verified', 'primary') - extra_kwargs = {'verified': {'read_only': True}} + fields = ("id", "email", "verified", "primary") + extra_kwargs = {"verified": {"read_only": True}} def validate(self, data): - data['user'] = self.context['user'] - instance = self.instance or self.Meta.model(**data) - instance.full_clean() - return data + data["user"] = self.context["user"] + return super().validate(data) diff --git a/openwisp_users/api/urls.py b/openwisp_users/api/urls.py index d5842cb92..a17e50be3 100644 --- a/openwisp_users/api/urls.py +++ b/openwisp_users/api/urls.py @@ -11,42 +11,42 @@ def get_api_urls(api_views=None): api_views = views urlpatterns += [ path( - 'users/organization/', + "users/organization/", views.organization_list, - name='organization_list', + name="organization_list", ), path( - 'users/organization//', + "users/organization//", views.organization_detail, - name='organization_detail', + name="organization_detail", ), path( - 'users/user/', + "users/user/", views.user_list, - name='user_list', + name="user_list", ), - path('users/user//', views.user_detail, name='user_detail'), + path("users/user//", views.user_detail, name="user_detail"), path( - 'users/user//password/', + "users/user//password/", views.change_password, - name='change_password', + name="change_password", ), path( - 'users/user//email/', + "users/user//email/", views.email_list, - name='email_list', + name="email_list", ), path( - 'users/user//email//', + "users/user//email//", views.email_update, - name='email_update', + name="email_update", ), - path('users/group/', views.group_list, name='group_list'), - path('users/group//', views.group_detail, name='group_detail'), + path("users/group/", views.group_list, name="group_list"), + path("users/group//", views.group_detail, name="group_detail"), ] if app_settings.USERS_AUTH_API: urlpatterns += [ - path('users/token/', views.obtain_auth_token, name='user_auth_token') + path("users/token/", views.obtain_auth_token, name="user_auth_token") ] return urlpatterns diff --git a/openwisp_users/api/views.py b/openwisp_users/api/views.py index cd09f7ea1..daad88c7f 100644 --- a/openwisp_users/api/views.py +++ b/openwisp_users/api/views.py @@ -34,10 +34,10 @@ from .swagger import ObtainTokenRequest, ObtainTokenResponse from .throttling import AuthRateThrottle -Group = load_model('openwisp_users', 'Group') -Organization = load_model('openwisp_users', 'Organization') +Group = load_model("openwisp_users", "Group") +Organization = load_model("openwisp_users", "Organization") User = get_user_model() -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') +OrganizationUser = load_model("openwisp_users", "OrganizationUser") class ProtectedAPIMixin(BaseProtectedAPIMixin): @@ -49,7 +49,7 @@ class ProtectedAPIMixin(BaseProtectedAPIMixin): class ListViewPagination(pagination.PageNumberPagination): page_size = 10 - page_size_query_param = 'page_size' + page_size_query_param = "page_size" max_page_size = 100 @@ -73,11 +73,11 @@ class BaseOrganizationView(ProtectedAPIMixin): def get_queryset(self): user = self.request.user if user.is_superuser: - return Organization.objects.order_by('-created') + return Organization.objects.order_by("-created") if user.is_anonymous: return return Organization.objects.filter(pk__in=user.organizations_managed).order_by( - '-created' + "-created" ) @@ -93,18 +93,18 @@ class BaseUserView(ProtectedAPIMixin): def get_queryset(self): user = self.request.user if user.is_superuser: - return User.objects.order_by('-date_joined') + return User.objects.order_by("-date_joined") if not user.is_superuser and not user.is_anonymous: org_users = OrganizationUser.objects.filter(user=user).select_related( - 'organization' + "organization" ) qs = User.objects.none() for org_user in org_users: if org_user.is_admin: qs = qs | org_user.organization.users.all().distinct() qs = qs.filter(is_superuser=False) - return qs.order_by('-date_joined') + return qs.order_by("-date_joined") class UsersListCreateView(BaseUserView, ListCreateAPIView): @@ -127,16 +127,16 @@ def get_serializer_class(self): class GroupListCreateView(ProtectedAPIMixin, ListCreateAPIView): queryset = Group.objects.prefetch_related( - 'permissions', 'permissions__content_type' - ).order_by('name') + "permissions", "permissions__content_type" + ).order_by("name") serializer_class = GroupSerializer pagination_class = ListViewPagination class GroupDetailView(ProtectedAPIMixin, RetrieveUpdateDestroyAPIView): queryset = Group.objects.prefetch_related( - 'permissions', 'permissions__content_type' - ).order_by('name') + "permissions", "permissions__content_type" + ).order_by("name") serializer_class = GroupSerializer @@ -158,14 +158,14 @@ def get_permissions(self): class if loggedin user wants to change his own password. """ - if str(self.request.user.id) == self.kwargs['pk']: + if str(self.request.user.id) == self.kwargs["pk"]: self.permission_classes = [IsAuthenticated] else: self.permission_classes = [IsAuthenticated, DjangoModelPermissions] return super(self.__class__, self).get_permissions() def get_object(self): - if getattr(self, 'swagger_fake_view', False): + if getattr(self, "swagger_fake_view", False): # To get rid of assertion error raised in # the dev server, and for schema generation return User.objects.none() @@ -178,14 +178,14 @@ def get_object(self): ): qs = qs | user filter_kwargs = { - 'id': self.kwargs['pk'], + "id": self.kwargs["pk"], } obj = get_object_or_404(qs, **filter_kwargs) return obj def get_serializer_context(self): context = super().get_serializer_context() - context['user'] = self.get_object() + context["user"] = self.get_object() return context def update(self, request, *args, **kwargs): @@ -193,7 +193,7 @@ def update(self, request, *args, **kwargs): serializer.is_valid(raise_exception=True) serializer.save() return Response( - {'status': 'Success', 'message': _('Password updated successfully')} + {"status": "Success", "message": _("Password updated successfully")} ) @@ -202,14 +202,14 @@ class BaseEmailView(ProtectedAPIMixin, FilterByParent, GenericAPIView): serializer_class = EmailAddressSerializer def get_queryset(self): - return EmailAddress.objects.select_related('user').order_by('id') + return EmailAddress.objects.select_related("user").order_by("id") def initial(self, *args, **kwargs): super().initial(*args, **kwargs) self.assert_parent_exists() def get_parent_queryset(self): - qs = User.objects.filter(pk=self.kwargs['pk']) + qs = User.objects.filter(pk=self.kwargs["pk"]) if self.request.user.is_superuser: return qs return self.get_organization_queryset(qs) @@ -219,19 +219,19 @@ def get_organization_queryset(self, qs): app_label = User._meta.app_config.label filter_kwargs = { # exclude superusers - 'is_superuser': False, + "is_superuser": False, # ensure user is member of the org - f'{app_label}_organizationuser__organization_id__in': orgs, + f"{app_label}_organizationuser__organization_id__in": orgs, } return qs.filter(**filter_kwargs).distinct() def get_serializer_context(self): - if getattr(self, 'swagger_fake_view', False): + if getattr(self, "swagger_fake_view", False): # To get rid of assertion error raised in # the dev server, and for schema generation return None context = super().get_serializer_context() - context['user'] = self.get_parent_queryset().first() + context["user"] = self.get_parent_queryset().first() return context @@ -239,11 +239,11 @@ class EmailListCreateView(BaseEmailView, ListCreateAPIView): pagination_class = ListViewPagination def get_queryset(self): - if getattr(self, 'swagger_fake_view', False): + if getattr(self, "swagger_fake_view", False): # To get rid of assertion error raised in # the dev server, and for schema generation return EmailAddress.objects.none() - return super().get_queryset().filter(user_id=self.kwargs['pk']) + return super().get_queryset().filter(user_id=self.kwargs["pk"]) class EmailUpdateView(BaseEmailView, RetrieveUpdateDestroyAPIView): @@ -251,7 +251,7 @@ def get_object(self): queryset = self.filter_queryset(self.get_queryset()) queryset = queryset.filter(user=self.get_parent_queryset().first()) filter_kwargs = { - 'id': self.kwargs['email_id'], + "id": self.kwargs["email_id"], } obj = get_object_or_404(queryset, **filter_kwargs) self.check_object_permissions(self.request, obj) diff --git a/openwisp_users/apps.py b/openwisp_users/apps.py index c82c168e5..6fb0f1187 100644 --- a/openwisp_users/apps.py +++ b/openwisp_users/apps.py @@ -18,10 +18,10 @@ class OpenwispUsersConfig(AppConfig): - name = 'openwisp_users' - app_label = 'openwisp_users' - verbose_name = _('Users and Organizations') - default_auto_field = 'django.db.models.AutoField' + name = "openwisp_users" + app_label = "openwisp_users" + verbose_name = _("Users and Organizations") + default_auto_field = "django.db.models.AutoField" def ready(self): self.register_menu_group() @@ -31,84 +31,84 @@ def ready(self): def register_menu_group(self): items = { 1: { - 'label': _('Users'), - 'model': settings.AUTH_USER_MODEL, - 'name': 'changelist', - 'icon': 'user', + "label": _("Users"), + "model": settings.AUTH_USER_MODEL, + "name": "changelist", + "icon": "user", }, 2: { - 'label': _('Organizations'), - 'model': get_model_name(self.app_label, 'Organization'), - 'name': 'changelist', - 'icon': 'ow-org', + "label": _("Organizations"), + "model": get_model_name(self.app_label, "Organization"), + "name": "changelist", + "icon": "ow-org", }, 3: { - 'label': _('Groups & Permissions'), - 'model': get_model_name(self.app_label, 'Group'), - 'name': 'changelist', - 'icon': 'ow-permission', + "label": _("Groups & Permissions"), + "model": get_model_name(self.app_label, "Group"), + "name": "changelist", + "icon": "ow-permission", }, } if app_settings.ORGANIZATION_OWNER_ADMIN: items[4] = { - 'label': _('Organization Owners'), - 'model': get_model_name(self.app_label, 'OrganizationOwner'), - 'name': 'changelist', - 'icon': 'ow-org-owner', + "label": _("Organization Owners"), + "model": get_model_name(self.app_label, "OrganizationOwner"), + "name": "changelist", + "icon": "ow-org-owner", } if app_settings.ORGANIZATION_USER_ADMIN: items[5] = { - 'label': _('Organization Users'), - 'model': get_model_name(self.app_label, 'OrganizationUser'), - 'name': 'changelist', - 'icon': 'ow-org-user', + "label": _("Organization Users"), + "model": get_model_name(self.app_label, "OrganizationUser"), + "name": "changelist", + "icon": "ow-org-user", } register_menu_group( position=40, config={ - 'label': _('Users & Organizations'), - 'items': items, - 'icon': 'ow-user-and-org', + "label": _("Users & Organizations"), + "items": items, + "icon": "ow-user-and-org", }, ) def set_default_settings(self): - LOGIN_URL = getattr(settings, 'LOGIN_URL', None) + LOGIN_URL = getattr(settings, "LOGIN_URL", None) if not LOGIN_URL: - setattr(settings, 'LOGIN_URL', 'account_login') + setattr(settings, "LOGIN_URL", "account_login") - LOGOUT_URL = getattr(settings, 'LOGOUT_URL', None) + LOGOUT_URL = getattr(settings, "LOGOUT_URL", None) if not LOGOUT_URL: - setattr(settings, 'LOGOUT_URL', 'account_logout') + setattr(settings, "LOGOUT_URL", "account_logout") if app_settings.USERS_AUTH_API and utils_settings.API_DOCS: - SWAGGER_SETTINGS = getattr(settings, 'SWAGGER_SETTINGS', {}) - SWAGGER_SETTINGS['SECURITY_DEFINITIONS'] = { - 'Bearer': {'type': 'apiKey', 'in': 'header', 'name': 'Authorization'} + SWAGGER_SETTINGS = getattr(settings, "SWAGGER_SETTINGS", {}) + SWAGGER_SETTINGS["SECURITY_DEFINITIONS"] = { + "Bearer": {"type": "apiKey", "in": "header", "name": "Authorization"} } - setattr(settings, 'SWAGGER_SETTINGS', SWAGGER_SETTINGS) + setattr(settings, "SWAGGER_SETTINGS", SWAGGER_SETTINGS) - ACCOUNT_ADAPTER = getattr(settings, 'ACCOUNT_ADAPTER', None) + ACCOUNT_ADAPTER = getattr(settings, "ACCOUNT_ADAPTER", None) if not ACCOUNT_ADAPTER: setattr( settings, - 'ACCOUNT_ADAPTER', - 'openwisp_users.accounts.adapter.EmailAdapter', + "ACCOUNT_ADAPTER", + "openwisp_users.accounts.adapter.EmailAdapter", ) def connect_receivers(self): - OrganizationUser = load_model('openwisp_users', 'OrganizationUser') - OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner') - Organization = load_model('openwisp_users', 'Organization') + OrganizationUser = load_model("openwisp_users", "OrganizationUser") + OrganizationOwner = load_model("openwisp_users", "OrganizationOwner") + Organization = load_model("openwisp_users", "Organization") signal_tuples = [ - (post_save, 'post_save'), - (post_delete, 'post_delete'), + (post_save, "post_save"), + (post_delete, "post_delete"), ] pre_save.connect( self.handle_org_is_active_change, sender=Organization, - dispatch_uid='handle_org_is_active_change', + dispatch_uid="handle_org_is_active_change", ) for model in [OrganizationUser, OrganizationOwner]: @@ -116,7 +116,7 @@ def connect_receivers(self): signal.connect( self.update_organizations_dict, sender=model, - dispatch_uid='{}_{}_update_organizations_dict'.format( + dispatch_uid="{}_{}_update_organizations_dict".format( name, model.__name__ ), ) @@ -128,14 +128,14 @@ def connect_receivers(self): pre_save.connect( self.pre_save_update_organizations_dict, sender=model, - dispatch_uid='{}_{}_pre_save_organizations_dict'.format( + dispatch_uid="{}_{}_pre_save_organizations_dict".format( name, model.__name__ ), ) post_save.connect( self.create_organization_owner, sender=OrganizationUser, - dispatch_uid='make_first_org_user_org_owner', + dispatch_uid="make_first_org_user_org_owner", ) @classmethod @@ -145,7 +145,7 @@ def handle_org_is_active_change(cls, instance, **kwargs): return Organization = instance._meta.model try: - old_instance = Organization.objects.only('is_active').get(pk=instance.pk) + old_instance = Organization.objects.only("is_active").get(pk=instance.pk) except Organization.DoesNotExist: return from .tasks import invalidate_org_membership_cache @@ -172,10 +172,10 @@ def _invalidate_old_related_obj_cache(instance, check_field): if getattr(db_obj, check_field) != getattr(instance, check_field): cls._invalidate_user_cache(getattr(db_obj, check_field)) - if hasattr(instance, 'user'): - _invalidate_old_related_obj_cache(instance, 'user') + if hasattr(instance, "user"): + _invalidate_old_related_obj_cache(instance, "user") else: - _invalidate_old_related_obj_cache(instance, 'organization_user') + _invalidate_old_related_obj_cache(instance, "organization_user") @classmethod def _invalidate_user_cache(cls, user): @@ -187,7 +187,7 @@ def _invalidate_user_cache(cls, user): @classmethod def update_organizations_dict(cls, instance, signal, **kwargs): - if hasattr(instance, 'user'): + if hasattr(instance, "user"): user = instance.user else: user = instance.organization_user.user @@ -199,7 +199,7 @@ def update_organizations_dict(cls, instance, signal, **kwargs): def create_organization_owner(cls, instance, created, **kwargs): if not created or not instance.is_admin: return - OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner') + OrganizationOwner = load_model("openwisp_users", "OrganizationOwner") org_owner_exist = OrganizationOwner.objects.filter( organization=instance.organization ).exists() @@ -213,7 +213,7 @@ def create_organization_owner(cls, instance, created, **kwargs): owner.save() except (ValidationError, IntegrityError) as e: logger.exception( - f'Got exception {type(e)} while saving ' - f'OrganizationOwner with organization_user {instance} and ' - f'organization {instance.organization}' + f"Got exception {type(e)} while saving " + f"OrganizationOwner with organization_user {instance} and " + f"organization {instance.organization}" ) diff --git a/openwisp_users/backends.py b/openwisp_users/backends.py index b7b54ea28..289839c15 100644 --- a/openwisp_users/backends.py +++ b/openwisp_users/backends.py @@ -23,16 +23,16 @@ def get_users(self, identifier): return User.objects.filter(conditions) def _get_phone_numbers(self, identifier): - prefixes = [''] + list(app_settings.AUTH_BACKEND_AUTO_PREFIXES) + prefixes = [""] + list(app_settings.AUTH_BACKEND_AUTO_PREFIXES) numbers = [identifier] found = [] # support those countries which use # leading zeros for their local numbers - if str(identifier).startswith('0'): + if str(identifier).startswith("0"): numbers.append(identifier[1:]) for prefix in prefixes: for number in numbers: - value = f'{prefix}{number}' + value = f"{prefix}{number}" try: phonenumbers.parse(value) found.append(value) diff --git a/openwisp_users/base/models.py b/openwisp_users/base/models.py index c4ec1ce97..0c558e9da 100644 --- a/openwisp_users/base/models.py +++ b/openwisp_users/base/models.py @@ -50,30 +50,30 @@ class AbstractUser(BaseUser): """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - email = models.EmailField(_('email address'), unique=True, blank=True, null=True) - bio = models.TextField(_('bio'), blank=True) - url = models.URLField(_('URL'), blank=True) - company = models.CharField(_('company'), max_length=30, blank=True) - location = models.CharField(_('location'), max_length=256, blank=True) + email = models.EmailField(_("email address"), unique=True, blank=True, null=True) + bio = models.TextField(_("bio"), blank=True) + url = models.URLField(_("URL"), blank=True) + company = models.CharField(_("company"), max_length=30, blank=True) + location = models.CharField(_("location"), max_length=256, blank=True) phone_number = PhoneNumberField( - _('phone number'), unique=True, blank=True, null=True + _("phone number"), unique=True, blank=True, null=True ) - birth_date = models.DateField(_('birth date'), blank=True, null=True) + birth_date = models.DateField(_("birth date"), blank=True, null=True) notes = models.TextField( - _('notes'), help_text=_('notes for internal usage'), blank=True + _("notes"), help_text=_("notes for internal usage"), blank=True ) language = models.CharField( max_length=8, choices=settings.LANGUAGES, default=settings.LANGUAGE_CODE, ) - password_updated = models.DateField(_('password updated'), blank=True, null=True) + password_updated = models.DateField(_("password updated"), blank=True, null=True) objects = UserManager() class Meta(BaseUser.Meta): abstract = True - indexes = [models.Index(fields=['id', 'email'], name='user_id_email_idx')] + indexes = [models.Index(fields=["id", "email"], name="user_id_email_idx")] @staticmethod def _get_pk(obj): @@ -85,7 +85,7 @@ def _get_pk(obj): elif obj is None: return None else: - raise ValueError('expected UUID, str or Organization instance') + raise ValueError("expected UUID, str or Organization instance") return str(pk) def set_password(self, *args, **kwargs): @@ -112,16 +112,16 @@ def is_member(self, organization): def is_manager(self, organization): org_dict = self.organizations_dict.get(self._get_pk(organization)) - return org_dict is not None and (org_dict['is_admin'] or org_dict['is_owner']) + return org_dict is not None and (org_dict["is_admin"] or org_dict["is_owner"]) def is_owner(self, organization): org_dict = self.organizations_dict.get(self._get_pk(organization)) - return org_dict is not None and org_dict['is_owner'] + return org_dict is not None and org_dict["is_owner"] @cached_property def is_owner_of_any_organization(self): for value in self.organizations_dict.values(): - if value['is_owner']: + if value["is_owner"]: return True return False @@ -131,23 +131,23 @@ def organizations_dict(self): Returns a dictionary which represents the organizations which the user is member of, or which the user manages or owns. """ - cache_key = 'user_{}_organizations'.format(self.pk) + cache_key = "user_{}_organizations".format(self.pk) organizations = cache.get(cache_key) if organizations is not None: return organizations - manager = load_model('openwisp_users', 'OrganizationUser').objects + manager = load_model("openwisp_users", "OrganizationUser").objects org_users = manager.filter( user=self, organization__is_active=True - ).select_related('organization', 'organizationowner') + ).select_related("organization", "organizationowner") organizations = {} for org_user in org_users: org = org_user.organization org_id = str(org.pk) organizations[org_id] = { - 'is_admin': org_user.is_admin, - 'is_owner': hasattr(org_user, 'organizationowner'), + "is_admin": org_user.is_admin, + "is_owner": hasattr(org_user, "organizationowner"), } cache.set(cache_key, organizations, 86400 * 2) # Cache for two days @@ -162,16 +162,16 @@ def __get_orgs(self, attribute): @cached_property def organizations_managed(self): - return self.__get_orgs('is_admin') + return self.__get_orgs("is_admin") @cached_property def organizations_owned(self): - return self.__get_orgs('is_owner') + return self.__get_orgs("is_owner") def clean(self): - if self.email == '': + if self.email == "": self.email = None - if self.phone_number == '': + if self.phone_number == "": self.phone_number = None if ( self.email @@ -180,14 +180,14 @@ def clean(self): .exists() ): raise ValidationError( - {'email': _('User with this Email address already exists.')} + {"email": _("User with this Email address already exists.")} ) def _invalidate_user_organizations_dict(self): """ Invalidate the organizations cache of the user """ - cache.delete(f'user_{self.pk}_organizations') + cache.delete(f"user_{self.pk}_organizations") try: del self.organizations_managed except AttributeError: @@ -206,8 +206,8 @@ class BaseGroup(object): class Meta: proxy = True - verbose_name = _('group') - verbose_name_plural = _('groups') + verbose_name = _("group") + verbose_name_plural = _("groups") class BaseOrganization(models.Model): @@ -216,14 +216,14 @@ class BaseOrganization(models.Model): """ id = models.UUIDField(primary_key=True, default=uuid.uuid4, editable=False) - description = models.TextField(_('description'), blank=True) - email = models.EmailField(_('email'), blank=True) - url = models.URLField(_('URL'), blank=True) + description = models.TextField(_("description"), blank=True) + email = models.EmailField(_("email"), blank=True) + url = models.URLField(_("URL"), blank=True) def __str__(self): value = self.name if not self.is_active: - value = '{0} ({1})'.format(value, _('disabled')) + value = "{0} ({1})".format(value, _("disabled")) return value class Meta: @@ -240,7 +240,7 @@ def add_user(self, user, is_admin=False, **kwargs): if not self.users.all().exists(): is_admin = True - OrganizationUser = load_model('openwisp_users', 'OrganizationUser') + OrganizationUser = load_model("openwisp_users", "OrganizationUser") return OrganizationUser.objects.create( user=user, organization=self, is_admin=is_admin ) @@ -264,8 +264,8 @@ def clean(self): ): raise ValidationError( _( - f'{self.user.username} is the owner of the organization: ' - f'{self.organization}, and cannot be downgraded' + f"{self.user.username} is the owner of the organization: " + f"{self.organization}, and cannot be downgraded" ) ) @@ -290,8 +290,8 @@ def clean(self): if self.organization_user.organization.pk != self.organization.pk: raise ValidationError( { - 'organization_user': _( - 'The selected user is not member of this organization.' + "organization_user": _( + "The selected user is not member of this organization." ) } ) diff --git a/openwisp_users/management/commands/export_users.py b/openwisp_users/management/commands/export_users.py index c5f98d199..8690e6c66 100644 --- a/openwisp_users/management/commands/export_users.py +++ b/openwisp_users/management/commands/export_users.py @@ -10,39 +10,39 @@ class Command(BaseCommand): - help = 'Exports user data to a CSV file' + help = "Exports user data to a CSV file" def add_arguments(self, parser): parser.add_argument( - '--exclude-fields', - dest='exclude_fields', - default='', - help='Comma-separated list of fields to exclude from export', + "--exclude-fields", + dest="exclude_fields", + default="", + help="Comma-separated list of fields to exclude from export", ) parser.add_argument( - '--filename', - dest='filename', - default='openwisp_exported_users.csv', + "--filename", + dest="filename", + default="openwisp_exported_users.csv", help=( - 'Filename for the exported CSV, defaults to' + "Filename for the exported CSV, defaults to" ' "openwisp_exported_users.csv"' ), ) def handle(self, *args, **options): - fields = app_settings.EXPORT_USERS_COMMAND_CONFIG.get('fields', []).copy() + fields = app_settings.EXPORT_USERS_COMMAND_CONFIG.get("fields", []).copy() # Get the fields to be excluded from the command-line argument - exclude_fields = options.get('exclude_fields').split(',') + exclude_fields = options.get("exclude_fields").split(",") # Remove excluded fields from the export fields fields = [field for field in fields if field not in exclude_fields] # Fetch all user data in a single query using select_related for related models queryset = User.objects.select_related( - *app_settings.EXPORT_USERS_COMMAND_CONFIG.get('select_related', []), - ).order_by('date_joined') + *app_settings.EXPORT_USERS_COMMAND_CONFIG.get("select_related", []), + ).order_by("date_joined") # Prepare a CSV writer - filename = options.get('filename') - csv_file = open(filename, 'w', newline='') + filename = options.get("filename") + csv_file = open(filename, "w", newline="") csv_writer = csv.writer(csv_file) # Write header row @@ -53,21 +53,21 @@ def handle(self, *args, **options): data_row = [] for field in fields: # Extract the value from related models - if '.' in field: - related_model, related_field = field.split('.') + if "." in field: + related_model, related_field = field.split(".") try: related_value = getattr( getattr(user, related_model), related_field ) except ObjectDoesNotExist: - data_row.append('') + data_row.append("") else: data_row.append(related_value) - elif field == 'organizations': + elif field == "organizations": organizations = [] for org_id, user_perm in user.organizations_dict.items(): organizations.append(f'({org_id},{user_perm["is_admin"]})') - data_row.append('\n'.join(organizations)) + data_row.append("\n".join(organizations)) else: data_row.append(getattr(user, field)) csv_writer.writerow(data_row) @@ -75,5 +75,5 @@ def handle(self, *args, **options): # Close the CSV file csv_file.close() self.stdout.write( - self.style.SUCCESS(f'User data exported successfully to {filename}!') + self.style.SUCCESS(f"User data exported successfully to {filename}!") ) diff --git a/openwisp_users/middleware.py b/openwisp_users/middleware.py index e3e9d69be..545014dd2 100644 --- a/openwisp_users/middleware.py +++ b/openwisp_users/middleware.py @@ -7,17 +7,17 @@ class PasswordExpirationMiddleware: exempted_url_names = [ - 'account_change_password', - 'admin:logout', - 'account_logout', - 'account_reset_password', - 'account_reset_password_done', - 'account_reset_password_from_key', - 'account_reset_password_from_key_done', + "account_change_password", + "admin:logout", + "account_logout", + "account_reset_password", + "account_reset_password_done", + "account_reset_password_from_key", + "account_reset_password_from_key_done", ] - admin_login_path = reverse_lazy('admin:login') - admin_index_path = reverse_lazy('admin:index') - account_change_password_path = reverse_lazy('account_change_password') + admin_login_path = reverse_lazy("admin:login") + admin_index_path = reverse_lazy("admin:index") + account_change_password_path = reverse_lazy("account_change_password") def __init__(self, get_response): self.get_response = get_response @@ -35,7 +35,7 @@ def __call__(self, request): ): messages.warning( request, - _('Your password has expired, please update your password.'), + _("Your password has expired, please update your password."), ) redirect_path = self.account_change_password_path if request.user.is_staff: @@ -44,6 +44,6 @@ def __call__(self, request): if request.path != self.admin_login_path else self.admin_index_path ) - redirect_path = f'{redirect_path}?{REDIRECT_FIELD_NAME}={next_path}' + redirect_path = f"{redirect_path}?{REDIRECT_FIELD_NAME}={next_path}" return redirect(redirect_path) return response diff --git a/openwisp_users/migrations/0001_initial.py b/openwisp_users/migrations/0001_initial.py index 2306674f2..432d74e7a 100644 --- a/openwisp_users/migrations/0001_initial.py +++ b/openwisp_users/migrations/0001_initial.py @@ -18,84 +18,84 @@ class Migration(migrations.Migration): initial = True - dependencies = [('auth', '0008_alter_user_username_max_length')] + dependencies = [("auth", "0008_alter_user_username_max_length")] operations = [ migrations.CreateModel( - name='User', + name="User", fields=[ - ('password', models.CharField(max_length=128, verbose_name='password')), + ("password", models.CharField(max_length=128, verbose_name="password")), ( - 'last_login', + "last_login", models.DateTimeField( - blank=True, null=True, verbose_name='last login' + blank=True, null=True, verbose_name="last login" ), ), ( - 'is_superuser', + "is_superuser", models.BooleanField( default=False, - help_text='Designates that this user has all permissions without explicitly assigning them.', - verbose_name='superuser status', + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", ), ), ( - 'username', + "username", models.CharField( error_messages={ - 'unique': 'A user with that username already exists.' + "unique": "A user with that username already exists." }, - help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', + help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", max_length=150, unique=True, validators=[ django.contrib.auth.validators.UnicodeUsernameValidator() ], - verbose_name='username', + verbose_name="username", ), ), ( - 'first_name', + "first_name", models.CharField( - blank=True, max_length=30, verbose_name='first name' + blank=True, max_length=30, verbose_name="first name" ), ), ( - 'last_name', + "last_name", models.CharField( - blank=True, max_length=30, verbose_name='last name' + blank=True, max_length=30, verbose_name="last name" ), ), ( - 'email', + "email", models.EmailField( - blank=True, max_length=254, verbose_name='email address' + blank=True, max_length=254, verbose_name="email address" ), ), ( - 'is_staff', + "is_staff", models.BooleanField( default=False, - help_text='Designates whether the user can log into this admin site.', - verbose_name='staff status', + help_text="Designates whether the user can log into this admin site.", + verbose_name="staff status", ), ), ( - 'is_active', + "is_active", models.BooleanField( default=True, - help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', - verbose_name='active', + help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", + verbose_name="active", ), ), ( - 'date_joined', + "date_joined", models.DateTimeField( - default=django.utils.timezone.now, verbose_name='date joined' + default=django.utils.timezone.now, verbose_name="date joined" ), ), ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -103,61 +103,61 @@ class Migration(migrations.Migration): serialize=False, ), ), - ('bio', models.TextField(blank=True, verbose_name='bio')), - ('url', models.URLField(blank=True, verbose_name='URL')), + ("bio", models.TextField(blank=True, verbose_name="bio")), + ("url", models.URLField(blank=True, verbose_name="URL")), ( - 'company', - models.CharField(blank=True, max_length=30, verbose_name='company'), + "company", + models.CharField(blank=True, max_length=30, verbose_name="company"), ), ( - 'location', + "location", models.CharField( - blank=True, max_length=128, verbose_name='location' + blank=True, max_length=128, verbose_name="location" ), ), ], options={ - 'verbose_name_plural': 'users', - 'verbose_name': 'user', - 'abstract': False, + "verbose_name_plural": "users", + "verbose_name": "user", + "abstract": False, }, - managers=[('objects', openwisp_users.base.models.UserManager())], + managers=[("objects", openwisp_users.base.models.UserManager())], ), migrations.CreateModel( - name='Organization', + name="Organization", fields=[ ( - 'name', + "name", models.CharField( - help_text='The name of the organization', max_length=200 + help_text="The name of the organization", max_length=200 ), ), - ('is_active', models.BooleanField(default=True)), + ("is_active", models.BooleanField(default=True)), ( - 'created', + "created", organizations.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False ), ), ( - 'modified', + "modified", organizations.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False ), ), ( - 'slug', + "slug", organizations.fields.SlugField( blank=True, editable=False, - help_text='The name in all lowercase, suitable for URL identification', + help_text="The name in all lowercase, suitable for URL identification", max_length=200, - populate_from='name', + populate_from="name", unique=True, ), ), ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -166,39 +166,39 @@ class Migration(migrations.Migration): ), ), ( - 'description', - models.TextField(blank=True, verbose_name='description'), + "description", + models.TextField(blank=True, verbose_name="description"), ), ( - 'email', - models.EmailField(blank=True, max_length=254, verbose_name='email'), + "email", + models.EmailField(blank=True, max_length=254, verbose_name="email"), ), - ('url', models.URLField(blank=True, verbose_name='URL')), + ("url", models.URLField(blank=True, verbose_name="URL")), ], options={ - 'verbose_name_plural': 'organizations', - 'verbose_name': 'organization', - 'abstract': False, - 'ordering': ['name'], + "verbose_name_plural": "organizations", + "verbose_name": "organization", + "abstract": False, + "ordering": ["name"], }, ), migrations.CreateModel( - name='OrganizationOwner', + name="OrganizationOwner", fields=[ ( - 'created', + "created", organizations.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False ), ), ( - 'modified', + "modified", organizations.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False ), ), ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -207,38 +207,38 @@ class Migration(migrations.Migration): ), ), ( - 'organization', + "organization", models.OneToOneField( on_delete=django.db.models.deletion.CASCADE, - related_name='owner', - to='openwisp_users.Organization', + related_name="owner", + to="openwisp_users.Organization", ), ), ], options={ - 'verbose_name_plural': 'organization owners', - 'verbose_name': 'organization owner', - 'abstract': False, + "verbose_name_plural": "organization owners", + "verbose_name": "organization owner", + "abstract": False, }, ), migrations.CreateModel( - name='OrganizationUser', + name="OrganizationUser", fields=[ ( - 'created', + "created", organizations.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False ), ), ( - 'modified', + "modified", organizations.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False ), ), - ('is_admin', models.BooleanField(default=False)), + ("is_admin", models.BooleanField(default=False)), ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -247,82 +247,82 @@ class Migration(migrations.Migration): ), ), ( - 'organization', + "organization", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='organization_users', - to='openwisp_users.Organization', + related_name="organization_users", + to="openwisp_users.Organization", ), ), ( - 'user', + "user", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='openwisp_users_organizationuser', + related_name="openwisp_users_organizationuser", to=settings.AUTH_USER_MODEL, ), ), ], options={ - 'verbose_name_plural': 'organization users', - 'verbose_name': 'organization user', - 'abstract': False, - 'ordering': ['organization', 'user'], + "verbose_name_plural": "organization users", + "verbose_name": "organization user", + "abstract": False, + "ordering": ["organization", "user"], }, ), migrations.CreateModel( - name='Group', + name="Group", fields=[], options={ - 'verbose_name_plural': 'groups', - 'verbose_name': 'group', - 'proxy': True, + "verbose_name_plural": "groups", + "verbose_name": "group", + "proxy": True, }, - bases=('auth.group',), - managers=[('objects', django.contrib.auth.models.GroupManager())], + bases=("auth.group",), + managers=[("objects", django.contrib.auth.models.GroupManager())], ), migrations.AddField( - model_name='organizationowner', - name='organization_user', + model_name="organizationowner", + name="organization_user", field=models.OneToOneField( on_delete=django.db.models.deletion.CASCADE, - to='openwisp_users.OrganizationUser', + to="openwisp_users.OrganizationUser", ), ), migrations.AddField( - model_name='organization', - name='users', + model_name="organization", + name="users", field=models.ManyToManyField( - related_name='openwisp_users_organization', - through='openwisp_users.OrganizationUser', + related_name="openwisp_users_organization", + through="openwisp_users.OrganizationUser", to=settings.AUTH_USER_MODEL, ), ), migrations.AddField( - model_name='user', - name='groups', + model_name="user", + name="groups", field=models.ManyToManyField( blank=True, - help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', - related_name='user_set', - related_query_name='user', - to='auth.Group', - verbose_name='groups', + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.Group", + verbose_name="groups", ), ), migrations.AddField( - model_name='user', - name='user_permissions', + model_name="user", + name="user_permissions", field=models.ManyToManyField( blank=True, - help_text='Specific permissions for this user.', - related_name='user_set', - related_query_name='user', - to='auth.Permission', - verbose_name='user permissions', + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.Permission", + verbose_name="user permissions", ), ), migrations.AlterUniqueTogether( - name='organizationuser', unique_together=set([('user', 'organization')]) + name="organizationuser", unique_together=set([("user", "organization")]) ), ] diff --git a/openwisp_users/migrations/0002_auto_20180508_2017.py b/openwisp_users/migrations/0002_auto_20180508_2017.py index df006eeea..3e740f497 100644 --- a/openwisp_users/migrations/0002_auto_20180508_2017.py +++ b/openwisp_users/migrations/0002_auto_20180508_2017.py @@ -4,14 +4,14 @@ class Migration(migrations.Migration): - dependencies = [('openwisp_users', '0001_initial')] + dependencies = [("openwisp_users", "0001_initial")] operations = [ migrations.AlterField( - model_name='user', - name='last_name', + model_name="user", + name="last_name", field=models.CharField( - blank=True, max_length=150, verbose_name='last name' + blank=True, max_length=150, verbose_name="last name" ), ) ] diff --git a/openwisp_users/migrations/0003_default_organization.py b/openwisp_users/migrations/0003_default_organization.py index f267c24a1..2d9c1e8b8 100644 --- a/openwisp_users/migrations/0003_default_organization.py +++ b/openwisp_users/migrations/0003_default_organization.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): - dependencies = [('openwisp_users', '0002_auto_20180508_2017')] + dependencies = [("openwisp_users", "0002_auto_20180508_2017")] operations = [ migrations.RunPython( diff --git a/openwisp_users/migrations/0004_default_groups.py b/openwisp_users/migrations/0004_default_groups.py index 2f994d1a7..da3082d7a 100644 --- a/openwisp_users/migrations/0004_default_groups.py +++ b/openwisp_users/migrations/0004_default_groups.py @@ -5,7 +5,7 @@ class Migration(migrations.Migration): - dependencies = [('openwisp_users', '0003_default_organization')] + dependencies = [("openwisp_users", "0003_default_organization")] operations = [ migrations.RunPython( diff --git a/openwisp_users/migrations/0005_user_phone_number.py b/openwisp_users/migrations/0005_user_phone_number.py index 77f753d6d..b305b6dd8 100644 --- a/openwisp_users/migrations/0005_user_phone_number.py +++ b/openwisp_users/migrations/0005_user_phone_number.py @@ -5,19 +5,19 @@ class Migration(migrations.Migration): - dependencies = [('openwisp_users', '0004_default_groups')] + dependencies = [("openwisp_users", "0004_default_groups")] operations = [ migrations.AddField( - model_name='user', - name='phone_number', + model_name="user", + name="phone_number", field=phonenumber_field.modelfields.PhoneNumberField( blank=True, unique=True, max_length=128, null=True, region=None, - verbose_name='phone number', + verbose_name="phone number", ), ) ] diff --git a/openwisp_users/migrations/0006_id_email_index_together.py b/openwisp_users/migrations/0006_id_email_index_together.py index b22d095a4..c1a694d6a 100644 --- a/openwisp_users/migrations/0006_id_email_index_together.py +++ b/openwisp_users/migrations/0006_id_email_index_together.py @@ -4,14 +4,14 @@ class Migration(migrations.Migration): - dependencies = [('openwisp_users', '0005_user_phone_number')] + dependencies = [("openwisp_users", "0005_user_phone_number")] operations = [ migrations.AddIndex( - model_name='user', + model_name="user", index=models.Index( - fields=['id', 'email'], - name='user_id_email_idx', + fields=["id", "email"], + name="user_id_email_idx", ), ), ] diff --git a/openwisp_users/migrations/0007_unique_email.py b/openwisp_users/migrations/0007_unique_email.py index 031478404..1574a90e0 100644 --- a/openwisp_users/migrations/0007_unique_email.py +++ b/openwisp_users/migrations/0007_unique_email.py @@ -2,25 +2,25 @@ def set_blank_emails_to_none(apps, schema_editor): - User = apps.get_model('openwisp_users', 'User') + User = apps.get_model("openwisp_users", "User") # if there is any user with blank email address # set its email address to None, otherwise the # unique constraint will be triggered by the empty string - for user in User.objects.filter(email=''): + for user in User.objects.filter(email=""): user.email = None user.save() class Migration(migrations.Migration): - dependencies = [('openwisp_users', '0006_id_email_index_together')] + dependencies = [("openwisp_users", "0006_id_email_index_together")] operations = [ # allow NULL migrations.AlterField( - model_name='user', - name='email', + model_name="user", + name="email", field=models.EmailField( - blank=True, max_length=254, null=True, verbose_name='email address' + blank=True, max_length=254, null=True, verbose_name="email address" ), ), # data migration to change empty strings to NULL @@ -29,14 +29,14 @@ class Migration(migrations.Migration): ), # add unique constraint migrations.AlterField( - model_name='user', - name='email', + model_name="user", + name="email", field=models.EmailField( blank=True, max_length=254, null=True, unique=True, - verbose_name='email address', + verbose_name="email address", ), ), ] diff --git a/openwisp_users/migrations/0008_update_admins_permissions.py b/openwisp_users/migrations/0008_update_admins_permissions.py index 1e8746dfe..970627376 100644 --- a/openwisp_users/migrations/0008_update_admins_permissions.py +++ b/openwisp_users/migrations/0008_update_admins_permissions.py @@ -4,7 +4,7 @@ class Migration(migrations.Migration): - dependencies = [('openwisp_users', '0007_unique_email')] + dependencies = [("openwisp_users", "0007_unique_email")] operations = [ migrations.RunPython( diff --git a/openwisp_users/migrations/0009_create_organization_owners.py b/openwisp_users/migrations/0009_create_organization_owners.py index f5bfebe29..5983cb3cb 100644 --- a/openwisp_users/migrations/0009_create_organization_owners.py +++ b/openwisp_users/migrations/0009_create_organization_owners.py @@ -6,6 +6,6 @@ class Migration(migrations.Migration): - dependencies = [('openwisp_users', '0008_update_admins_permissions')] + dependencies = [("openwisp_users", "0008_update_admins_permissions")] operations = [migrations.RunPython(create_organization_owners)] diff --git a/openwisp_users/migrations/0010_allow_admins_change_organization.py b/openwisp_users/migrations/0010_allow_admins_change_organization.py index ea5aa99c3..6db6eeecf 100644 --- a/openwisp_users/migrations/0010_allow_admins_change_organization.py +++ b/openwisp_users/migrations/0010_allow_admins_change_organization.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): - dependencies = [('openwisp_users', '0009_create_organization_owners')] + dependencies = [("openwisp_users", "0009_create_organization_owners")] operations = [ migrations.RunPython( diff --git a/openwisp_users/migrations/0011_user_first_name_150_max_length.py b/openwisp_users/migrations/0011_user_first_name_150_max_length.py index f65d2adc7..2c5953068 100644 --- a/openwisp_users/migrations/0011_user_first_name_150_max_length.py +++ b/openwisp_users/migrations/0011_user_first_name_150_max_length.py @@ -4,14 +4,14 @@ class Migration(migrations.Migration): - dependencies = [('openwisp_users', '0010_allow_admins_change_organization')] + dependencies = [("openwisp_users", "0010_allow_admins_change_organization")] operations = [ migrations.AlterField( - model_name='user', - name='first_name', + model_name="user", + name="first_name", field=models.CharField( - blank=True, max_length=150, verbose_name='first name' + blank=True, max_length=150, verbose_name="first name" ), ) ] diff --git a/openwisp_users/migrations/0012_user_location_max_length_256.py b/openwisp_users/migrations/0012_user_location_max_length_256.py index aba40b691..7d3276bef 100644 --- a/openwisp_users/migrations/0012_user_location_max_length_256.py +++ b/openwisp_users/migrations/0012_user_location_max_length_256.py @@ -5,13 +5,13 @@ class Migration(migrations.Migration): dependencies = [ - ('openwisp_users', '0011_user_first_name_150_max_length'), + ("openwisp_users", "0011_user_first_name_150_max_length"), ] operations = [ migrations.AlterField( - model_name='user', - name='location', - field=models.CharField(blank=True, max_length=256, verbose_name='location'), + model_name="user", + name="location", + field=models.CharField(blank=True, max_length=256, verbose_name="location"), ), ] diff --git a/openwisp_users/migrations/0013_user_birth_date.py b/openwisp_users/migrations/0013_user_birth_date.py index 589d256ec..d288a7483 100644 --- a/openwisp_users/migrations/0013_user_birth_date.py +++ b/openwisp_users/migrations/0013_user_birth_date.py @@ -5,13 +5,13 @@ class Migration(migrations.Migration): dependencies = [ - ('openwisp_users', '0012_user_location_max_length_256'), + ("openwisp_users", "0012_user_location_max_length_256"), ] operations = [ migrations.AddField( - model_name='user', - name='birth_date', - field=models.DateField(blank=True, null=True, verbose_name='birth date'), + model_name="user", + name="birth_date", + field=models.DateField(blank=True, null=True, verbose_name="birth date"), ), ] diff --git a/openwisp_users/migrations/0014_user_notes.py b/openwisp_users/migrations/0014_user_notes.py index 4af95ed0e..6db53d01f 100644 --- a/openwisp_users/migrations/0014_user_notes.py +++ b/openwisp_users/migrations/0014_user_notes.py @@ -5,17 +5,17 @@ class Migration(migrations.Migration): dependencies = [ - ('openwisp_users', '0013_user_birth_date'), + ("openwisp_users", "0013_user_birth_date"), ] operations = [ migrations.AddField( - model_name='user', - name='notes', + model_name="user", + name="notes", field=models.TextField( blank=True, - help_text='notes for internal usage', - verbose_name='notes', + help_text="notes for internal usage", + verbose_name="notes", ), ), ] diff --git a/openwisp_users/migrations/0015_alter_organization_users_alter_organizationuser_user.py b/openwisp_users/migrations/0015_alter_organization_users_alter_organizationuser_user.py index fa1af6fd7..3e573699a 100644 --- a/openwisp_users/migrations/0015_alter_organization_users_alter_organizationuser_user.py +++ b/openwisp_users/migrations/0015_alter_organization_users_alter_organizationuser_user.py @@ -7,26 +7,26 @@ class Migration(migrations.Migration): dependencies = [ - ('openwisp_users', '0014_user_notes'), + ("openwisp_users", "0014_user_notes"), ] operations = [ migrations.AlterField( - model_name='organization', - name='users', + model_name="organization", + name="users", field=models.ManyToManyField( - related_name='%(app_label)s_%(class)s', - through='openwisp_users.OrganizationUser', + related_name="%(app_label)s_%(class)s", + through="openwisp_users.OrganizationUser", to=settings.AUTH_USER_MODEL, ), ), migrations.AlterField( - model_name='organizationuser', - name='user', + model_name="organizationuser", + name="user", field=models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='%(app_label)s_%(class)s', - to='openwisp_users.user', + related_name="%(app_label)s_%(class)s", + to="openwisp_users.user", ), ), ] diff --git a/openwisp_users/migrations/0016_organizationinvitation.py b/openwisp_users/migrations/0016_organizationinvitation.py index 70e6d0cc2..e6344e1a9 100644 --- a/openwisp_users/migrations/0016_organizationinvitation.py +++ b/openwisp_users/migrations/0016_organizationinvitation.py @@ -8,71 +8,71 @@ class Migration(migrations.Migration): dependencies = [ - ('openwisp_users', '0015_alter_organization_users_alter_organizationuser_user'), + ("openwisp_users", "0015_alter_organization_users_alter_organizationuser_user"), ] operations = [ migrations.CreateModel( - name='OrganizationInvitation', + name="OrganizationInvitation", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('guid', models.UUIDField(editable=False)), + ("guid", models.UUIDField(editable=False)), ( - 'invitee_identifier', + "invitee_identifier", models.CharField( - help_text='The contact identifier for the invitee, email, phone number, social media handle, etc.', + help_text="The contact identifier for the invitee, email, phone number, social media handle, etc.", max_length=1000, ), ), ( - 'created', + "created", organizations.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False ), ), ( - 'modified', + "modified", organizations.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False ), ), ( - 'invited_by', + "invited_by", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='%(app_label)s_%(class)s_sent_invitations', - to='openwisp_users.user', + related_name="%(app_label)s_%(class)s_sent_invitations", + to="openwisp_users.user", ), ), ( - 'invitee', + "invitee", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - related_name='%(app_label)s_%(class)s_invitations', - to='openwisp_users.user', + related_name="%(app_label)s_%(class)s_invitations", + to="openwisp_users.user", ), ), ( - 'organization', + "organization", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='organization_invites', - to='openwisp_users.organization', + related_name="organization_invites", + to="openwisp_users.organization", ), ), ], options={ - 'abstract': False, + "abstract": False, }, ), ] diff --git a/openwisp_users/migrations/0017_user_language.py b/openwisp_users/migrations/0017_user_language.py index 8ec964429..bd78e0bea 100644 --- a/openwisp_users/migrations/0017_user_language.py +++ b/openwisp_users/migrations/0017_user_language.py @@ -6,13 +6,13 @@ class Migration(migrations.Migration): dependencies = [ - ('openwisp_users', '0016_organizationinvitation'), + ("openwisp_users", "0016_organizationinvitation"), ] operations = [ migrations.AddField( - model_name='user', - name='language', + model_name="user", + name="language", field=models.CharField( choices=settings.LANGUAGES, default=settings.LANGUAGE_CODE, diff --git a/openwisp_users/migrations/0018_allow_operator_view_organisation.py b/openwisp_users/migrations/0018_allow_operator_view_organisation.py index 8ca7ed3c5..31f1b082a 100644 --- a/openwisp_users/migrations/0018_allow_operator_view_organisation.py +++ b/openwisp_users/migrations/0018_allow_operator_view_organisation.py @@ -4,7 +4,7 @@ class Migration(migrations.Migration): - dependencies = [('openwisp_users', '0017_user_language')] + dependencies = [("openwisp_users", "0017_user_language")] operations = [ migrations.RunPython( diff --git a/openwisp_users/migrations/0021_rename_user_id_email_openwisp_us_id_06c07a_idx.py b/openwisp_users/migrations/0021_rename_user_id_email_openwisp_us_id_06c07a_idx.py index f6405749d..ae5f9dc3f 100644 --- a/openwisp_users/migrations/0021_rename_user_id_email_openwisp_us_id_06c07a_idx.py +++ b/openwisp_users/migrations/0021_rename_user_id_email_openwisp_us_id_06c07a_idx.py @@ -5,13 +5,13 @@ class Migration(migrations.Migration): dependencies = [ - ('openwisp_users', '0020_populate_password_updated_field'), + ("openwisp_users", "0020_populate_password_updated_field"), ] operations = [ migrations.RenameIndex( - model_name='user', - new_name='user_id_email_idx', - old_fields=('id', 'email'), + model_name="user", + new_name="user_id_email_idx", + old_fields=("id", "email"), ), ] diff --git a/openwisp_users/migrations/__init__.py b/openwisp_users/migrations/__init__.py index 7428baf14..520422e80 100644 --- a/openwisp_users/migrations/__init__.py +++ b/openwisp_users/migrations/__init__.py @@ -10,17 +10,17 @@ def set_default_organization_uuid(apps, schema_editor): Get or create a default organization then set settings._OPENWISP_DEFAULT_ORG_UUID """ - org_model = swapper.get_model_name('openwisp_users', 'organization') + org_model = swapper.get_model_name("openwisp_users", "organization") model_app_label = swapper.split(org_model)[0] - organization = apps.get_model(model_app_label, 'organization') + organization = apps.get_model(model_app_label, "organization") default_organization = organization.objects.first() if default_organization is None: default_organization = organization( - name='default', - slug='default', - description='This is the default organization. ' - 'It was created automatically during installation. ' - 'You can simply rename it to your organization name.', + name="default", + slug="default", + description="This is the default organization. " + "It was created automatically during installation. " + "You can simply rename it to your organization name.", ) default_organization.full_clean() default_organization.save() @@ -32,9 +32,9 @@ def set_default_organization_uuid(apps, schema_editor): def create_default_groups(apps, schema_editor): - org_model = swapper.get_model_name('openwisp_users', 'organization') + org_model = swapper.get_model_name("openwisp_users", "organization") model_app_label = swapper.split(org_model)[0] - group = apps.get_model(model_app_label, 'group') + group = apps.get_model(model_app_label, "group") # To populate all the permissions for app_config in apps.get_app_configs(): @@ -42,43 +42,43 @@ def create_default_groups(apps, schema_editor): create_permissions(app_config, apps=apps, verbosity=0) app_config.models_module = None - operator = group.objects.filter(name='Operator') + operator = group.objects.filter(name="Operator") if operator.count() == 0: - operator = group.objects.create(name='Operator') + operator = group.objects.create(name="Operator") - admin = group.objects.filter(name='Administrator') + admin = group.objects.filter(name="Administrator") if admin.count() == 0: - admin = group.objects.create(name='Administrator') + admin = group.objects.create(name="Administrator") permissions = [ Permission.objects.get( - content_type__app_label=model_app_label, codename='add_user' + content_type__app_label=model_app_label, codename="add_user" ).pk, Permission.objects.get( - content_type__app_label=model_app_label, codename='change_user' + content_type__app_label=model_app_label, codename="change_user" ).pk, Permission.objects.get( content_type__app_label=model_app_label, - codename='change_organizationuser', + codename="change_organizationuser", ).pk, Permission.objects.get( content_type__app_label=model_app_label, - codename='delete_organizationuser', + codename="delete_organizationuser", ).pk, Permission.objects.get( - content_type__app_label=model_app_label, codename='add_organizationuser' + content_type__app_label=model_app_label, codename="add_organizationuser" ).pk, ] try: permissions += [ Permission.objects.get( - content_type__app_label=model_app_label, codename='view_user' + content_type__app_label=model_app_label, codename="view_user" ).pk, Permission.objects.get( - content_type__app_label=model_app_label, codename='view_group' + content_type__app_label=model_app_label, codename="view_group" ).pk, Permission.objects.get( content_type__app_label=model_app_label, - codename='view_organizationuser', + codename="view_organizationuser", ).pk, ] except Permission.DoesNotExist: @@ -87,25 +87,25 @@ def create_default_groups(apps, schema_editor): def update_admins_permissions(apps, schema_editor): - org_model = swapper.get_model_name('openwisp_users', 'organization') + org_model = swapper.get_model_name("openwisp_users", "organization") model_app_label = swapper.split(org_model)[0] - group = apps.get_model(model_app_label, 'group') - email_model = swapper.get_model_name('account', 'EmailAddress') + group = apps.get_model(model_app_label, "group") + email_model = swapper.get_model_name("account", "EmailAddress") email_app_label = swapper.split(email_model)[0] try: - admin = group.objects.get(name='Administrator') + admin = group.objects.get(name="Administrator") permissions = [ Permission.objects.get( - content_type__app_label=email_app_label, codename='view_emailaddress' + content_type__app_label=email_app_label, codename="view_emailaddress" ).pk, Permission.objects.get( - content_type__app_label=email_app_label, codename='delete_emailaddress' + content_type__app_label=email_app_label, codename="delete_emailaddress" ).pk, Permission.objects.get( - content_type__app_label=email_app_label, codename='change_emailaddress' + content_type__app_label=email_app_label, codename="change_emailaddress" ).pk, Permission.objects.get( - content_type__app_label=model_app_label, codename='delete_user' + content_type__app_label=model_app_label, codename="delete_user" ).pk, ] admin.permissions.add(*permissions) @@ -114,19 +114,19 @@ def update_admins_permissions(apps, schema_editor): def get_model(apps, name): - model_name = swapper.get_model_name('openwisp_users', name) + model_name = swapper.get_model_name("openwisp_users", name) model_label = swapper.split(model_name)[0] return apps.get_model(model_label, name) def create_organization_owners(apps, schema_editor): - OrganizationOwner = get_model(apps, 'OrganizationOwner') - OrganizationUser = get_model(apps, 'OrganizationUser') - Organization = get_model(apps, 'Organization') + OrganizationOwner = get_model(apps, "OrganizationOwner") + OrganizationUser = get_model(apps, "OrganizationUser") + Organization = get_model(apps, "Organization") for org in Organization.objects.all(): org_user = ( OrganizationUser.objects.filter(organization=org, is_admin=True) - .order_by('created') + .order_by("created") .first() ) if not OrganizationOwner.objects.filter(organization=org).exists() and org_user: @@ -136,17 +136,17 @@ def create_organization_owners(apps, schema_editor): def allow_admins_change_organization(apps, schema_editor): - Group = get_model(apps, 'Group') + Group = get_model(apps, "Group") try: - admins = Group.objects.get(name='Administrator') + admins = Group.objects.get(name="Administrator") permissions = [ Permission.objects.get( content_type__app_label=Group._meta.app_label, - codename='change_organization', + codename="change_organization", ).pk, Permission.objects.get( content_type__app_label=Group._meta.app_label, - codename='change_organizationowner', + codename="change_organizationowner", ).pk, ] admins.permissions.add(*permissions) @@ -155,10 +155,10 @@ def allow_admins_change_organization(apps, schema_editor): def allow_operator_view_organization(apps, schema_editor): - Group = get_model(apps, 'Group') + Group = get_model(apps, "Group") try: - operator = Group.objects.get(name='Operator') - permissions = [Permission.objects.get(codename='view_organization').pk] + operator = Group.objects.get(name="Operator") + permissions = [Permission.objects.get(codename="view_organization").pk] operator.permissions.add(*permissions) except ObjectDoesNotExist: pass diff --git a/openwisp_users/mixins.py b/openwisp_users/mixins.py index d1120882a..bd3e3caf7 100644 --- a/openwisp_users/mixins.py +++ b/openwisp_users/mixins.py @@ -13,7 +13,7 @@ class ValidateOrgMixin(object): - implements ``_validate_org_relation`` method """ - def _validate_org_relation(self, rel, field_error='organization'): + def _validate_org_relation(self, rel, field_error="organization"): """ if the relation is owned by a specific organization this object must be related to the same organization @@ -28,8 +28,8 @@ def _validate_org_relation(self, rel, field_error='organization'): and str(self.organization_id) != str(rel.organization_id) ): message = _( - 'Please ensure that the organization of this {object_label} ' - 'and the organization of the related {related_object_label} match.' + "Please ensure that the organization of this {object_label} " + "and the organization of the related {related_object_label} match." ) message = message.format( object_label=self._meta.verbose_name, @@ -37,7 +37,7 @@ def _validate_org_relation(self, rel, field_error='organization'): ) raise ValidationError({field_error: message}) - def _validate_org_reverse_relation(self, rel_name, field_error='organization'): + def _validate_org_reverse_relation(self, rel_name, field_error="organization"): """ prevents changing organization for existing objects which have relations specified by ``rel_name`` pointing to them, @@ -60,11 +60,11 @@ def _validate_org_reverse_relation(self, rel_name, field_error='organization'): related_label = ( rel_meta.verbose_name if count == 1 else rel_meta.verbose_name_plural ) - verb = _('is') if count == 1 else _('are') + verb = _("is") if count == 1 else _("are") message = _( - 'The organization of this {object_label} cannot be changed ' - 'because {0} {related_object_label} {verb} still ' - 'related to it'.format( + "The organization of this {object_label} cannot be changed " + "because {0} {related_object_label} {verb} still " + "related to it".format( count, object_label=self._meta.verbose_name, related_object_label=related_label, @@ -82,8 +82,8 @@ class OrgMixin(ValidateOrgMixin, models.Model): """ organization = models.ForeignKey( - get_model_name('openwisp_users', 'Organization'), - verbose_name=_('organization'), + get_model_name("openwisp_users", "Organization"), + verbose_name=_("organization"), on_delete=models.CASCADE, ) @@ -101,6 +101,6 @@ class Meta: abstract = True -_org_field = ShareableOrgMixin._meta.get_field('organization') +_org_field = ShareableOrgMixin._meta.get_field("organization") _org_field.blank = True _org_field.null = True diff --git a/openwisp_users/models.py b/openwisp_users/models.py index cdba3f57d..266ad047b 100644 --- a/openwisp_users/models.py +++ b/openwisp_users/models.py @@ -23,17 +23,17 @@ class Meta(AbstractUser.Meta): class Organization(BaseOrganization, AbstractOrganization): class Meta(AbstractOrganization.Meta): - swapper.swappable_setting('openwisp_users', 'Organization') + swapper.swappable_setting("openwisp_users", "Organization") class OrganizationUser(BaseOrganizationUser, AbstractOrganizationUser): class Meta(AbstractOrganizationUser.Meta): - swapper.swappable_setting('openwisp_users', 'OrganizationUser') + swapper.swappable_setting("openwisp_users", "OrganizationUser") class OrganizationOwner(BaseOrganizationOwner, AbstractOrganizationOwner): class Meta(AbstractOrganizationOwner.Meta): - swapper.swappable_setting('openwisp_users', 'OrganizationOwner') + swapper.swappable_setting("openwisp_users", "OrganizationOwner") # only needed for compatibility with django-organizations~=2.x @@ -43,9 +43,9 @@ class Meta(AbstractOrganizationOwner.Meta): # https://django-organizations.readthedocs.io/ class OrganizationInvitation(AbstractOrganizationInvitation): class Meta(AbstractOrganizationInvitation.Meta): - swapper.swappable_setting('openwisp_users', 'OrganizationInvitation') + swapper.swappable_setting("openwisp_users", "OrganizationInvitation") class Group(BaseGroup, AbstractGroup): class Meta(BaseGroup.Meta): - swapper.swappable_setting('openwisp_users', 'Group') + swapper.swappable_setting("openwisp_users", "Group") diff --git a/openwisp_users/multitenancy.py b/openwisp_users/multitenancy.py index c27c73c34..b6557fb23 100644 --- a/openwisp_users/multitenancy.py +++ b/openwisp_users/multitenancy.py @@ -8,7 +8,7 @@ from .widgets import SHARED_SYSTEMWIDE_LABEL, OrganizationAutocompleteSelect User = get_user_model() -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') +OrganizationUser = load_model("openwisp_users", "OrganizationUser") class MultitenantAdminMixin(object): @@ -35,7 +35,7 @@ def __init__(self, *args, **kwargs): def get_repr(self, obj): return str(obj) - get_repr.short_description = _('name') + get_repr.short_description = _("name") def get_queryset(self, request): """ @@ -48,14 +48,14 @@ def get_queryset(self, request): return self.multitenant_behaviour_for_user_admin(request) if user.is_superuser: return qs - if hasattr(self.model, 'organization'): + if hasattr(self.model, "organization"): return qs.filter(organization__in=user.organizations_managed) - if self.model.__name__ == 'Organization': + if self.model.__name__ == "Organization": return qs.filter(pk__in=user.organizations_managed) elif not self.multitenant_parent: return qs else: - qsarg = '{0}__organization__in'.format(self.multitenant_parent) + qsarg = "{0}__organization__in".format(self.multitenant_parent) return qs.filter(**{qsarg: user.organizations_managed}) def _edit_form(self, request, form): @@ -70,7 +70,7 @@ def _edit_form(self, request, form): """ fields = form.base_fields user = request.user - org_field = fields.get('organization') + org_field = fields.get("organization") if user.is_superuser and org_field and not org_field.required: org_field.empty_label = SHARED_SYSTEMWIDE_LABEL elif not user.is_superuser: @@ -123,7 +123,7 @@ def multitenant_behaviour_for_user_admin(self, request): OrganizationUser.objects.filter( organization_id__in=user.organizations_managed ) - .values_list('user_id') + .values_list("user_id") .distinct() ) # hide superusers from organization operators @@ -131,9 +131,9 @@ def multitenant_behaviour_for_user_admin(self, request): return qs.filter(id__in=user_ids, is_superuser=False) def formfield_for_foreignkey(self, db_field, request, **kwargs): - if db_field.name == 'organization': - kwargs['widget'] = OrganizationAutocompleteSelect( - db_field, self.admin_site, using=kwargs.get('using') + if db_field.name == "organization": + kwargs["widget"] = OrganizationAutocompleteSelect( + db_field, self.admin_site, using=kwargs.get("using") ) return super().formfield_for_foreignkey(db_field, request, **kwargs) @@ -144,12 +144,12 @@ class MultitenantOrgFilter(AutocompleteFilter): user is associated with in its available choices """ - field_name = 'organization' - parameter_name = 'organization' - org_lookup = 'id__in' - title = _('organization') + field_name = "organization" + parameter_name = "organization" + org_lookup = "id__in" + title = _("organization") widget_attrs = AutocompleteFilter.widget_attrs.copy() - widget_attrs.update({'data-empty-label': SHARED_SYSTEMWIDE_LABEL}) + widget_attrs.update({"data-empty-label": SHARED_SYSTEMWIDE_LABEL}) class MultitenantRelatedOrgFilter(MultitenantOrgFilter): @@ -158,4 +158,4 @@ class MultitenantRelatedOrgFilter(MultitenantOrgFilter): one of the organizations the current user is associated with """ - org_lookup = 'organization__in' + org_lookup = "organization__in" diff --git a/openwisp_users/password_validation.py b/openwisp_users/password_validation.py index 7205569e4..a3e021694 100644 --- a/openwisp_users/password_validation.py +++ b/openwisp_users/password_validation.py @@ -14,8 +14,8 @@ def validate(self, password, user=None): if user.check_password(password): # The new password is same as the current password raise ValidationError( - _('You cannot re-use your current password. Enter a new password.') + _("You cannot re-use your current password. Enter a new password.") ) def get_help_text(self): - return _('Your password cannot be the same as your current password.') + return _("Your password cannot be the same as your current password.") diff --git a/openwisp_users/settings.py b/openwisp_users/settings.py index f78568d12..0b49fb5a6 100644 --- a/openwisp_users/settings.py +++ b/openwisp_users/settings.py @@ -2,50 +2,50 @@ from openwisp_utils.utils import default_or_test -ORGANIZATION_USER_ADMIN = getattr(settings, 'OPENWISP_ORGANIZATION_USER_ADMIN', True) -ORGANIZATION_OWNER_ADMIN = getattr(settings, 'OPENWISP_ORGANIZATION_OWNER_ADMIN', True) -USERS_AUTH_API = getattr(settings, 'OPENWISP_USERS_AUTH_API', True) +ORGANIZATION_USER_ADMIN = getattr(settings, "OPENWISP_ORGANIZATION_USER_ADMIN", True) +ORGANIZATION_OWNER_ADMIN = getattr(settings, "OPENWISP_ORGANIZATION_OWNER_ADMIN", True) +USERS_AUTH_API = getattr(settings, "OPENWISP_USERS_AUTH_API", True) USERS_AUTH_THROTTLE_RATE = getattr( settings, - 'OPENWISP_USERS_AUTH_THROTTLE_RATE', - default_or_test(value='20/day', test=None), + "OPENWISP_USERS_AUTH_THROTTLE_RATE", + default_or_test(value="20/day", test=None), ) AUTH_BACKEND_AUTO_PREFIXES = getattr( - settings, 'OPENWISP_USERS_AUTH_BACKEND_AUTO_PREFIXES', tuple() + settings, "OPENWISP_USERS_AUTH_BACKEND_AUTO_PREFIXES", tuple() ) EXPORT_USERS_COMMAND_CONFIG = { - 'fields': [ - 'id', - 'username', - 'email', - 'password', - 'first_name', - 'last_name', - 'is_staff', - 'is_active', - 'date_joined', - 'phone_number', - 'birth_date', - 'location', - 'notes', - 'language', - 'organizations', + "fields": [ + "id", + "username", + "email", + "password", + "first_name", + "last_name", + "is_staff", + "is_active", + "date_joined", + "phone_number", + "birth_date", + "location", + "notes", + "language", + "organizations", ], - 'select_related': [], + "select_related": [], } USER_PASSWORD_EXPIRATION = getattr( - settings, 'OPENWISP_USERS_USER_PASSWORD_EXPIRATION', 0 + settings, "OPENWISP_USERS_USER_PASSWORD_EXPIRATION", 0 ) STAFF_USER_PASSWORD_EXPIRATION = getattr( - settings, 'OPENWISP_USERS_STAFF_USER_PASSWORD_EXPIRATION', 0 + settings, "OPENWISP_USERS_STAFF_USER_PASSWORD_EXPIRATION", 0 ) # Set the AutocompleteFilter view if it is not defined in the settings setattr( settings, - 'OPENWISP_AUTOCOMPLETE_FILTER_VIEW', + "OPENWISP_AUTOCOMPLETE_FILTER_VIEW", getattr( settings, - 'OPENWISP_AUTOCOMPLETE_FILTER_VIEW', - 'openwisp_users.views.AutocompleteJsonView', + "OPENWISP_AUTOCOMPLETE_FILTER_VIEW", + "openwisp_users.views.AutocompleteJsonView", ), ) diff --git a/openwisp_users/tasks.py b/openwisp_users/tasks.py index aa43d78d2..9806c77ef 100644 --- a/openwisp_users/tasks.py +++ b/openwisp_users/tasks.py @@ -18,7 +18,7 @@ from . import settings as app_settings User = get_user_model() -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') +OrganizationUser = load_model("openwisp_users", "OrganizationUser") @shared_task @@ -61,22 +61,22 @@ def password_expiration_email(): for user in qs.iterator(): with translation.override(user.language): send_email( - subject=_('Action Required: Password Expiry Notice'), + subject=_("Action Required: Password Expiry Notice"), body_text=render_to_string( - 'account/email/password_expiration_message.txt', - context={'username': user.username, 'expiry_date': expiry_date}, + "account/email/password_expiration_message.txt", + context={"username": user.username, "expiry_date": expiry_date}, ).strip(), body_html=render_to_string( - 'account/email/password_expiration_message.html', - context={'username': user.username, 'expiry_date': expiry_date}, + "account/email/password_expiration_message.html", + context={"username": user.username, "expiry_date": expiry_date}, ).strip(), recipients=[user.email], extra_context={ - 'call_to_action_url': 'https://{0}{1}'.format( + "call_to_action_url": "https://{0}{1}".format( current_site.domain, - reverse('account_change_password'), + reverse("account_change_password"), ), - 'call_to_action_text': _('Change password'), + "call_to_action_text": _("Change password"), }, ) # Avoid overloading the SMTP server by sending multiple @@ -97,6 +97,6 @@ def invalidate_org_membership_cache(organization_pk): """ qs = OrganizationUser.objects.filter( organization_id=organization_pk - ).select_related('user') + ).select_related("user") for org_user in qs.iterator(): org_user.user._invalidate_user_organizations_dict() diff --git a/openwisp_users/tests/test_accounts.py b/openwisp_users/tests/test_accounts.py index d4db9d743..4fd2626b5 100644 --- a/openwisp_users/tests/test_accounts.py +++ b/openwisp_users/tests/test_accounts.py @@ -14,15 +14,15 @@ class TestAccountView(TestOrganizationMixin, TestCase): - def _login_user(self, username='tester', password='tester'): + def _login_user(self, username="tester", password="tester"): response = self.client.post( - reverse('account_login'), - data={'login': username, 'password': password}, + reverse("account_login"), + data={"login": username, "password": password}, follow=True, ) return response - @patch.object(app_settings, 'USER_PASSWORD_EXPIRATION', 30) + @patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 30) def test_password_expired_user_logins(self): self._create_org_user() User.objects.update(password_updated=now() - timedelta(days=60)) @@ -33,12 +33,12 @@ def test_password_expired_user_logins(self): '
    \n' '
  • Successfully signed in as tester.
  • \n\n' '
  • Your password has expired, please update ' - 'your password.
  • \n
' + "your password.\n" ), html=True, ) self.assertEqual( - response.request.get('PATH_INFO'), reverse('account_change_password') + response.request.get("PATH_INFO"), reverse("account_change_password") ) # Password expired users can browse accounts views self.assertContains( @@ -52,38 +52,38 @@ def _test_expired_user_password_reset(self, user): self.assertEqual(user.has_password_expired(), True) response = self.client.post( reverse( - 'account_reset_password', + "account_reset_password", ), - data={'email': user.email}, + data={"email": user.email}, follow=True, ) self.assertEqual(response.status_code, 200) self.assertEqual( - response.request.get('PATH_INFO'), reverse('account_reset_password_done') + response.request.get("PATH_INFO"), reverse("account_reset_password_done") ) - self.assertContains(response, 'We have sent you an email') + self.assertContains(response, "We have sent you an email") email = mail.outbox.pop() - password_reset_url = re.search(r'https?://[^\s]+', email.body).group(0) + password_reset_url = re.search(r"https?://[^\s]+", email.body).group(0) response = self.client.get( password_reset_url, ) response = self.client.post( response.url, - data={'password1': 'newpassword', 'password2': 'newpassword'}, + data={"password1": "newpassword", "password2": "newpassword"}, follow=True, ) self.assertEqual(response.status_code, 200) self.assertEqual( - response.request.get('PATH_INFO'), - reverse('account_reset_password_from_key_done'), + response.request.get("PATH_INFO"), + reverse("account_reset_password_from_key_done"), ) - @patch.object(app_settings, 'USER_PASSWORD_EXPIRATION', 30) + @patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 30) def test_password_expired_user_reset_password(self): user = self._create_org_user().user self._test_expired_user_password_reset(user) - @patch.object(app_settings, 'USER_PASSWORD_EXPIRATION', 30) + @patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 30) def test_password_expired_user_reset_password_after_login(self): user = self._create_org_user().user self._login_user() @@ -105,15 +105,15 @@ def _test_login_flow(self): response, ( '
  • Your password has expired, please update ' - 'your password at http://testserver/accounts/password/change/
  • ' + "your password at http://testserver/accounts/password/change/" ), ) - @patch.object(app_settings, 'USER_PASSWORD_EXPIRATION', 0) + @patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 0) def test_user_login_password_expiration_disabled(self): self._test_login_flow() - @patch.object(app_settings, 'USER_PASSWORD_EXPIRATION', 90) + @patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 90) def test_user_login_password_expiration_enabled(self): self._test_login_flow() @@ -121,23 +121,23 @@ def test_redirection_to_success_page_after_password_update(self): user = self._create_operator() self.client.force_login(user) response = self.client.post( - reverse('account_change_password'), + reverse("account_change_password"), data={ - 'oldpassword': 'tester', - 'password1': 'newpassword', - 'password2': 'newpassword', + "oldpassword": "tester", + "password1": "newpassword", + "password2": "newpassword", }, follow=True, ) - self.assertContains(response, 'Your password has been changed successfully.') - self.assertContains(response, 'This web page can be closed.') + self.assertContains(response, "Your password has been changed successfully.") + self.assertContains(response, "This web page can be closed.") def test_inactive_user_login(self): self._create_org_user() User.objects.update(is_active=False) response = self._login_user() self.assertContains( - response, 'The username and/or password you specified are not correct.' + response, "The username and/or password you specified are not correct." ) def test_social_login_user_change_password(self): @@ -153,15 +153,15 @@ def test_social_login_user_change_password(self): user.set_unusable_password() user.save() self.client.force_login(user) - response = self.client.get(reverse('account_change_password')) + response = self.client.get(reverse("account_change_password")) self.assertEqual(response.status_code, 200) self.assertContains( response, ( - '

    You cannot change your password from this application because' - ' your account is linked to a third-party authentication provider.

    ' - '

    Please visit the provider\'s website to manage your password.

    ' - '

    This web page can be closed.

    ' + "

    You cannot change your password from this application because" + " your account is linked to a third-party authentication provider.

    " + "

    Please visit the provider's website to manage your password.

    " + "

    This web page can be closed.

    " ), html=True, ) diff --git a/openwisp_users/tests/test_adapter.py b/openwisp_users/tests/test_adapter.py index ab39ad1b5..9057358c5 100644 --- a/openwisp_users/tests/test_adapter.py +++ b/openwisp_users/tests/test_adapter.py @@ -20,26 +20,26 @@ def test_template_not_present(self): with self.assertRaises(TemplateDoesNotExist): EmailAdapter.send_mail(self, template_prefix, email, {}) - @mock.patch('openwisp_users.accounts.adapter.send_email') + @mock.patch("openwisp_users.accounts.adapter.send_email") def test_assertion_not_raised_when_html_template_missing(self, mail_func): self._create_user() - queryset = User.objects.filter(username='tester') + queryset = User.objects.filter(username="tester") self.assertEqual(queryset.count(), 1) - params = {'email': 'test@tester.com'} - self.client.post(reverse('account_reset_password'), params, follow=True) + params = {"email": "test@tester.com"} + self.client.post(reverse("account_reset_password"), params, follow=True) send_mail_calls = mail_func.call_args_list send_mail_arguments = send_mail_calls[0][0] - self.assertEqual(send_mail_arguments[0], '[example.com] Password Reset Email') - self.assertEqual(send_mail_arguments[2], '') + self.assertEqual(send_mail_arguments[0], "[example.com] Password Reset Email") + self.assertEqual(send_mail_arguments[2], "") def test_password_reset_email_sent(self): self._create_user() - queryset = User.objects.filter(username='tester') + queryset = User.objects.filter(username="tester") self.assertEqual(queryset.count(), 1) - params = {'email': 'test@tester.com'} - self.client.post(reverse('account_reset_password'), params, follow=True) + params = {"email": "test@tester.com"} + self.client.post(reverse("account_reset_password"), params, follow=True) self.assertEqual(len(mail.outbox), 1) email = mail.outbox.pop() self.assertFalse(email.alternatives) - self.assertIn('Password Reset Email', email.subject) - self.assertIn('Click the link below to reset your password', email.body) + self.assertIn("Password Reset Email", email.subject) + self.assertIn("Click the link below to reset your password", email.body) diff --git a/openwisp_users/tests/test_admin.py b/openwisp_users/tests/test_admin.py index 92873a775..f5568a787 100644 --- a/openwisp_users/tests/test_admin.py +++ b/openwisp_users/tests/test_admin.py @@ -29,11 +29,11 @@ TestUserAdditionalFieldsMixin, ) -Organization = load_model('openwisp_users', 'Organization') -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') -OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner') +Organization = load_model("openwisp_users", "Organization") +OrganizationUser = load_model("openwisp_users", "OrganizationUser") +OrganizationOwner = load_model("openwisp_users", "OrganizationOwner") User = get_user_model() -Group = load_model('openwisp_users', 'Group') +Group = load_model("openwisp_users", "Group") class TestUsersAdmin( @@ -44,7 +44,7 @@ class TestUsersAdmin( ): """test admin site""" - app_label = 'openwisp_users' + app_label = "openwisp_users" is_integration_test = False def assertNumQueries(self, num, using=DEFAULT_DB_ALIAS, func=None, *args, **kwargs): @@ -74,42 +74,42 @@ def _get_user_edit_form_inline_params(self, user, organization): @property def add_user_inline_params(self): return { - 'emailaddress_set-TOTAL_FORMS': 0, - 'emailaddress_set-INITIAL_FORMS': 0, - 'emailaddress_set-MIN_NUM_FORMS': 0, - 'emailaddress_set-MAX_NUM_FORMS': 0, - f'{self.app_label}_organizationuser-TOTAL_FORMS': 0, - f'{self.app_label}_organizationuser-INITIAL_FORMS': 0, - f'{self.app_label}_organizationuser-MIN_NUM_FORMS': 0, - f'{self.app_label}_organizationuser-MAX_NUM_FORMS': 0, + "emailaddress_set-TOTAL_FORMS": 0, + "emailaddress_set-INITIAL_FORMS": 0, + "emailaddress_set-MIN_NUM_FORMS": 0, + "emailaddress_set-MAX_NUM_FORMS": 0, + f"{self.app_label}_organizationuser-TOTAL_FORMS": 0, + f"{self.app_label}_organizationuser-INITIAL_FORMS": 0, + f"{self.app_label}_organizationuser-MIN_NUM_FORMS": 0, + f"{self.app_label}_organizationuser-MAX_NUM_FORMS": 0, } def test_admin_add_user_auto_email(self): admin = self._create_admin() self.client.force_login(admin) params = dict( - username='testadd', - email='test@testadd.com', - password1='tester', - password2='tester', + username="testadd", + email="test@testadd.com", + password1="tester", + password2="tester", ) params.update(self.add_user_inline_params) params.update(self._additional_params_add()) - self.client.post(reverse(f'admin:{self.app_label}_user_add'), params) - queryset = User.objects.filter(username='testadd') + self.client.post(reverse(f"admin:{self.app_label}_user_add"), params) + queryset = User.objects.filter(username="testadd") self.assertEqual(queryset.count(), 1) user = queryset.first() self.assertEqual(user.emailaddress_set.count(), 1) emailaddress = user.emailaddress_set.first() - self.assertEqual(emailaddress.email, 'test@testadd.com') + self.assertEqual(emailaddress.email, "test@testadd.com") self.assertEqual(len(mail.outbox), 1) with self.subTest("Test HTML Template used for Signup Mail"): email = mail.outbox.pop() self.assertTrue(email.alternatives) - self.assertIn('testadd', email.alternatives[0][0]) + self.assertIn("testadd", email.alternatives[0][0]) self.assertIn( - 'To confirm this is correct, please click on the button below', + "To confirm this is correct, please click on the button below", email.alternatives[0][0], ) @@ -117,124 +117,124 @@ def test_admin_add_user_empty_email(self): admin = self._create_admin() self.client.force_login(admin) params = dict( - username='testadd', email='', password1='tester', password2='tester' + username="testadd", email="", password1="tester", password2="tester" ) params.update(self.add_user_inline_params) params.update(self._additional_params_add()) - response = self.client.post(reverse(f'admin:{self.app_label}_user_add'), params) - queryset = User.objects.filter(username='testadd') + response = self.client.post(reverse(f"admin:{self.app_label}_user_add"), params) + queryset = User.objects.filter(username="testadd") self.assertEqual(queryset.count(), 0) - self.assertContains(response, 'errors field-email') + self.assertContains(response, "errors field-email") self.assertEqual(len(mail.outbox), 0) def test_admin_change_user_auto_email(self): admin = self._create_admin() self._create_org_user(user=admin) self.client.force_login(admin) - user = self._create_user(email='old@mail.com', username='changemailtest') + user = self._create_user(email="old@mail.com", username="changemailtest") params = user.__dict__ - params['email'] = 'new@mail.com' - params.pop('phone_number') - params.pop('password', None) - params.pop('_password', None) - params.pop('last_login') - params.pop('password_updated') + params["email"] = "new@mail.com" + params.pop("phone_number") + params.pop("password", None) + params.pop("_password", None) + params.pop("last_login") + params.pop("password_updated") params = self._additional_params_pop(params) # inline emails params.update(self.add_user_inline_params) params.update( { - 'emailaddress_set-TOTAL_FORMS': 1, - 'emailaddress_set-INITIAL_FORMS': 1, - 'emailaddress_set-0-verified': True, - 'emailaddress_set-0-primary': True, - 'emailaddress_set-0-id': user.emailaddress_set.first().id, - 'emailaddress_set-0-user': user.id, + "emailaddress_set-TOTAL_FORMS": 1, + "emailaddress_set-INITIAL_FORMS": 1, + "emailaddress_set-0-verified": True, + "emailaddress_set-0-primary": True, + "emailaddress_set-0-id": user.emailaddress_set.first().id, + "emailaddress_set-0-user": user.id, } ) params.update(self._get_user_edit_form_inline_params(user, self._get_org())) response = self.client.post( - reverse(f'admin:{self.app_label}_user_change', args=[user.pk]), + reverse(f"admin:{self.app_label}_user_change", args=[user.pk]), params, follow=True, ) - self.assertNotContains(response, 'Please correct the error below.') - user = User.objects.get(username='changemailtest') + self.assertNotContains(response, "Please correct the error below.") + user = User.objects.get(username="changemailtest") email_set = user.emailaddress_set self.assertEqual(email_set.count(), 2) - self.assertEqual(email_set.filter(email='new@mail.com').count(), 1) + self.assertEqual(email_set.filter(email="new@mail.com").count(), 1) self.assertEqual(len(mail.outbox), 1) def test_admin_change_user_email_empty(self): - admin = self._create_admin(email='') + admin = self._create_admin(email="") self._create_org_user(user=admin) self.client.force_login(admin) params = dict( - username='testchange', - email='', - first_name='', - last_name='', - bio='', - url='', - company='', - location='', + username="testchange", + email="", + first_name="", + last_name="", + bio="", + url="", + company="", + location="", ) params.update(self.add_user_inline_params) params.update(self._get_user_edit_form_inline_params(admin, self._get_org())) response = self.client.post( - reverse(f'admin:{self.app_label}_user_change', args=[admin.pk]), params + reverse(f"admin:{self.app_label}_user_change", args=[admin.pk]), params ) - queryset = User.objects.filter(username='testchange') + queryset = User.objects.filter(username="testchange") self.assertEqual(queryset.count(), 0) self.assertEqual(len(mail.outbox), 0) - self.assertContains(response, 'errors field-email') + self.assertContains(response, "errors field-email") def test_admin_change_user_page_get_invalid_UUID(self): admin = self._create_admin() self.client.force_login(admin) - with self.subTest('Test for wrong identifier'): + with self.subTest("Test for wrong identifier"): response = self.client.get( - reverse(f'admin:{self.app_label}_user_change', args=['WRONG']), + reverse(f"admin:{self.app_label}_user_change", args=["WRONG"]), follow=True, ) - content = 'User with ID “WRONG” doesn’t exist. Perhaps it was deleted?' + content = "User with ID “WRONG” doesn’t exist. Perhaps it was deleted?" self.assertContains(response, content, status_code=200) - with self.subTest('Test for non-existing user'): + with self.subTest("Test for non-existing user"): id = uuid.uuid4() response = self.client.get( reverse( - f'admin:{self.app_label}_user_change', + f"admin:{self.app_label}_user_change", args=[id], ), follow=True, ) - content = f'User with ID “{id}” doesn’t exist. Perhaps it was deleted?' + content = f"User with ID “{id}” doesn’t exist. Perhaps it was deleted?" self.assertContains(response, content, status_code=200) def test_admin_change_user_password_updated(self): admin = self._create_admin() # User.objects.create_user does not execute User.set_password # which is required for setting User.password_updated field - admin.set_password('tester') + admin.set_password("tester") admin.save() self.client.force_login(admin) response = self.client.get( - reverse(f'admin:{self.app_label}_user_change', args=[admin.pk]), + reverse(f"admin:{self.app_label}_user_change", args=[admin.pk]), ) self.assertContains( response, ( - '\n\n' + "\n\n" f'
    {date(now())}
    ' ), html=True, ) def test_admin_change_user_reuse_password(self): - admin = self._create_admin(password='tester') + admin = self._create_admin(password="tester") self.client.force_login(admin) - path = reverse('admin:auth_user_password_change', args=[admin.pk]) - data = {'password1': 'tester', 'password2': 'tester'} + path = reverse("admin:auth_user_password_change", args=[admin.pk]) + data = {"password1": "tester", "password2": "tester"} with override_settings( AUTH_PASSWORD_VALIDATORS=[ { @@ -254,10 +254,10 @@ def test_admin_change_user_reuse_password(self): response, ( '
    • ' - 'You cannot re-use your current password. ' - 'Enter a new password.
    ' + "You cannot re-use your current password. " + "Enter a new password." ).format( - ' id="id_password2_error"' if django.VERSION >= (5, 2) else '' + ' id="id_password2_error"' if django.VERSION >= (5, 2) else "" ), ) with override_settings(AUTH_PASSWORD_VALIDATORS=[]): @@ -270,8 +270,8 @@ def test_admin_change_user_reuse_password(self): response, ( '
    • ' - 'You cannot re-use your current password. ' - 'Enter a new password.
    ' + "You cannot re-use your current password. " + "Enter a new password." ), ) self.assertContains( @@ -283,9 +283,9 @@ def test_organization_view_on_site(self): self.client.force_login(admin) org = self._create_org() response = self.client.get( - reverse(f'admin:{self.app_label}_organization_change', args=[org.pk]) + reverse(f"admin:{self.app_label}_organization_change", args=[org.pk]) ) - self.assertNotContains(response, 'viewsitelink') + self.assertNotContains(response, "viewsitelink") def test_organization_user_view_on_site(self): admin = self._create_admin() @@ -293,15 +293,15 @@ def test_organization_user_view_on_site(self): org = self._create_org() ou = self._create_org_user(organization=org, user=admin) response = self.client.get( - reverse(f'admin:{self.app_label}_organizationuser_change', args=[ou.pk]) + reverse(f"admin:{self.app_label}_organizationuser_change", args=[ou.pk]) ) - self.assertNotContains(response, 'viewsitelink') + self.assertNotContains(response, "viewsitelink") def test_admin_change_user_is_superuser_editable(self): admin = self._create_admin() self.client.force_login(admin) response = self.client.get( - reverse(f'admin:{self.app_label}_user_change', args=[admin.pk]) + reverse(f"admin:{self.app_label}_user_change", args=[admin.pk]) ) html = '' @@ -327,7 +327,7 @@ def test_admin_change_user_permissions_editable(self): admin = self._create_admin() self.client.force_login(admin) response = self.client.get( - reverse(f'admin:{self.app_label}_user_change', args=[admin.pk]) + reverse(f"admin:{self.app_label}_user_change", args=[admin.pk]) ) html = '' '', + "", ) response = self.client.get( reverse( - f'admin:{self.app_label}_organizationuser_change', args=[org_user2.pk] + f"admin:{self.app_label}_organizationuser_change", args=[org_user2.pk] ) ) self.assertEqual(response.status_code, 302) def test_admin_operator_delete_org_user(self): - org1 = self._create_org(name='test-org1') - org2 = self._create_org(name='test-org2') + org1 = self._create_org(name="test-org1") + org2 = self._create_org(name="test-org2") operator = self._create_operator_with_user_permissions() org_permissions = Permission.objects.filter( - codename__endswith='organization_user' + codename__endswith="organization_user" ) operator.user_permissions.add(*org_permissions) - options1 = {'organization': org1, 'is_admin': True, 'user': operator} - options2 = {'organization': org2, 'is_admin': False, 'user': operator} + options1 = {"organization": org1, "is_admin": True, "user": operator} + options2 = {"organization": org2, "is_admin": False, "user": operator} org_user1 = self._create_org_user(**options1) org_user2 = self._create_org_user(**options2) self.client.force_login(operator) response = self.client.get( reverse( - f'admin:{self.app_label}_organizationuser_change', args=[org_user1.pk] + f"admin:{self.app_label}_organizationuser_change", args=[org_user1.pk] ) ) self.assertContains( @@ -522,7 +522,7 @@ def test_admin_operator_delete_org_user(self): ) response = self.client.get( reverse( - f'admin:{self.app_label}_organizationuser_change', args=[org_user2.pk] + f"admin:{self.app_label}_organizationuser_change", args=[org_user2.pk] ) ) self.assertEqual(response.status_code, 302) @@ -530,26 +530,26 @@ def test_admin_operator_delete_org_user(self): def test_admin_changelist_superuser_column_visible(self): admin = self._create_admin() self.client.force_login(admin) - response = self.client.get(reverse(f'admin:{self.app_label}_user_changelist')) - self.assertContains(response, 'Superuser status') + response = self.client.get(reverse(f"admin:{self.app_label}_user_changelist")) + self.assertContains(response, "Superuser status") def test_admin_operator_change_superuser_forbidden(self): admin = self._create_admin() operator = self._create_operator_with_user_permissions() options = { - 'organization': self._get_org(), - 'is_admin': True, - 'user': self._get_operator(), + "organization": self._get_org(), + "is_admin": True, + "user": self._get_operator(), } self._create_org_user(**options) self.client.force_login(operator) response = self.client.get( - reverse(f'admin:{self.app_label}_user_change', args=[operator.pk]) + reverse(f"admin:{self.app_label}_user_change", args=[operator.pk]) ) self.assertEqual(response.status_code, 200) # operator trying to acess change form of superuser gets redirected response = self.client.get( - reverse(f'admin:{self.app_label}_user_change', args=[admin.pk]) + reverse(f"admin:{self.app_label}_user_change", args=[admin.pk]) ) self.assertEqual(response.status_code, 302) @@ -557,103 +557,103 @@ def test_new_user_email_exists(self): admin = self._create_admin() self.client.force_login(admin) params = dict( - username='testadd', - email='test@testadd.com', - password1='tester', - password2='tester', + username="testadd", + email="test@testadd.com", + password1="tester", + password2="tester", ) params.update(self.add_user_inline_params) params.update(self._additional_params_add()) - self.client.post(reverse(f'admin:{self.app_label}_user_add'), params) - res = self.client.post(reverse(f'admin:{self.app_label}_user_add'), params) + self.client.post(reverse(f"admin:{self.app_label}_user_add"), params) + res = self.client.post(reverse(f"admin:{self.app_label}_user_add"), params) self.assertContains( - res, '
  • User with this Email address already exists.
  • ' + res, "
  • User with this Email address already exists.
  • " ) def test_create_user_existing_mail_different_case(self): - self._create_user(username='user1', email='user@example.com') + self._create_user(username="user1", email="user@example.com") admin = self._create_admin() self.client.force_login(admin) params = dict( - username='testadd', - email='USER@example.com', - password1='tester', - password2='tester', + username="testadd", + email="USER@example.com", + password1="tester", + password2="tester", ) params.update(self.add_user_inline_params) - res = self.client.post(reverse(f'admin:{self.app_label}_user_add'), params) - content = '
  • User with this Email address already exists.
  • ' + res = self.client.post(reverse(f"admin:{self.app_label}_user_add"), params) + content = "
  • User with this Email address already exists.
  • " self.assertContains(res, content, status_code=200) def test_update_user_no_validation_error(self): admin = self._create_admin() self.client.force_login(admin) - user = self._create_user(email='user@example.com', username='user1') + user = self._create_user(email="user@example.com", username="user1") params = user.__dict__ - params['username'] = 'user2' - params.pop('last_login') - params.pop('password_updated') - params.pop('phone_number') - params.pop('password', None) - params.pop('_password', None) + params["username"] = "user2" + params.pop("last_login") + params.pop("password_updated") + params.pop("phone_number") + params.pop("password", None) + params.pop("_password", None) params = self._additional_params_pop(params) params.update(self.add_user_inline_params) params.update( { - 'emailaddress_set-TOTAL_FORMS': 1, - 'emailaddress_set-INITIAL_FORMS': 1, - 'emailaddress_set-0-verified': True, - 'emailaddress_set-0-primary': True, - 'emailaddress_set-0-id': user.emailaddress_set.first().id, - 'emailaddress_set-0-user': user.id, + "emailaddress_set-TOTAL_FORMS": 1, + "emailaddress_set-INITIAL_FORMS": 1, + "emailaddress_set-0-verified": True, + "emailaddress_set-0-primary": True, + "emailaddress_set-0-id": user.emailaddress_set.first().id, + "emailaddress_set-0-user": user.id, } ) params.update(self._get_user_edit_form_inline_params(user, self._get_org())) res = self.client.post( - reverse(f'admin:{self.app_label}_user_change', args=[user.pk]), + reverse(f"admin:{self.app_label}_user_change", args=[user.pk]), params, follow=True, ) user.refresh_from_db() self.assertNotIn( - '
  • User with this Email address already exists.
  • ', + "
  • User with this Email address already exists.
  • ", res.content.decode(), ) - self.assertEqual(user.username, 'user2') + self.assertEqual(user.username, "user2") def test_edit_user_email_exists(self): admin = self._create_admin() self.client.force_login(admin) self._get_org_user() - user = self._create_user(email='asd@asd.com', username='newTester') + user = self._create_user(email="asd@asd.com", username="newTester") self._create_org_user(user=user) params = user.__dict__ - params['email'] = 'test@tester.com' - params.pop('phone_number') - params.pop('password', None) - params.pop('_password', None) - params.pop('last_login') - params.pop('password_updated') + params["email"] = "test@tester.com" + params.pop("phone_number") + params.pop("password", None) + params.pop("_password", None) + params.pop("last_login") + params.pop("password_updated") params = self._additional_params_pop(params) params.update(self.add_user_inline_params) params.update( { - 'emailaddress_set-TOTAL_FORMS': 1, - 'emailaddress_set-INITIAL_FORMS': 1, - 'emailaddress_set-0-verified': True, - 'emailaddress_set-0-primary': True, - 'emailaddress_set-0-id': user.emailaddress_set.first().id, - 'emailaddress_set-0-user': user.id, + "emailaddress_set-TOTAL_FORMS": 1, + "emailaddress_set-INITIAL_FORMS": 1, + "emailaddress_set-0-verified": True, + "emailaddress_set-0-primary": True, + "emailaddress_set-0-id": user.emailaddress_set.first().id, + "emailaddress_set-0-user": user.id, } ) params.update(self._get_user_edit_form_inline_params(user, self._get_org())) res = self.client.post( - reverse(f'admin:{self.app_label}_user_change', args=[user.pk]), + reverse(f"admin:{self.app_label}_user_change", args=[user.pk]), params, follow=True, ) self.assertContains( - res, '
  • User with this Email address already exists.
  • ' + res, "
  • User with this Email address already exists.
  • " ) def test_change_staff_without_group(self): @@ -661,19 +661,19 @@ def test_change_staff_without_group(self): user = self._create_user(is_staff=True) self._create_org_user(user=user) params = user.__dict__ - params.pop('password', None) - params.pop('_password', None) - params.pop('last_login') - params.pop('password_updated') - params.pop('phone_number') + params.pop("password", None) + params.pop("_password", None) + params.pop("last_login") + params.pop("password_updated") + params.pop("phone_number") params.update(self.add_user_inline_params) params.update(self._additional_params_add()) params.update(self._get_user_edit_form_inline_params(user, self._get_org())) - path = reverse(f'admin:{self.app_label}_user_change', args=[user.pk]) + path = reverse(f"admin:{self.app_label}_user_change", args=[user.pk]) r = self.client.post(path, params, follow=True) self.assertEqual(r.status_code, 200) self.assertContains( - r, 'A staff user must belong to a group, please select one.' + r, "A staff user must belong to a group, please select one." ) user.refresh_from_db() self.assertEqual(user.groups.count(), 0) @@ -683,24 +683,24 @@ def test_change_staff_with_group(self): user = self._create_operator() org = self._get_org() self._create_org_user(organization=org, user=user) - group = Group.objects.get(name='Administrator') + group = Group.objects.get(name="Administrator") params = user.__dict__ - params['groups'] = str(group.pk) - params.pop('phone_number') - params.pop('password', None) - params.pop('_password', None) - params.pop('last_login') - params.pop('password_updated') + params["groups"] = str(group.pk) + params.pop("phone_number") + params.pop("password", None) + params.pop("_password", None) + params.pop("last_login") + params.pop("password_updated") params.update(self.add_user_inline_params) params.update(self._additional_params_add()) params.update(self._get_user_edit_form_inline_params(user, org)) - path = reverse(f'admin:{self.app_label}_user_change', args=[user.pk]) + path = reverse(f"admin:{self.app_label}_user_change", args=[user.pk]) r = self.client.post(path, params, follow=True) self.assertEqual(r.status_code, 200) - self.assertNotContains(r, 'Please correct the error below.') + self.assertNotContains(r, "Please correct the error below.") user.refresh_from_db() self.assertEqual(user.groups.count(), 1) - self.assertEqual(user.groups.get(name='Administrator').pk, group.pk) + self.assertEqual(user.groups.get(name="Administrator").pk, group.pk) def test_staff_cannot_edit_org_owner(self): user1 = self._create_user( @@ -712,11 +712,11 @@ def test_staff_cannot_edit_org_owner(self): org = self._get_org() org_user2 = self._create_org_user(user=user2, organization=org, is_admin=True) self._create_org_user(user=user1, organization=org, is_admin=True) - group = Group.objects.filter(name='Administrator') + group = Group.objects.filter(name="Administrator") user1.groups.set(group) user2.groups.set(group) self.client.force_login(user1) - path = reverse(f'admin:{self.app_label}_user_change', args=[user2.pk]) + path = reverse(f"admin:{self.app_label}_user_change", args=[user2.pk]) r = self.client.get(path) self.assertEqual(r.status_code, 200) self.assertContains(r, f'class="readonly">{user2.username}') @@ -728,7 +728,7 @@ def test_staff_cannot_edit_org_owner(self): org_owner = OrganizationOwner.objects.get(organization_user=org_user2) org_owner.delete() - path = reverse(f'admin:{self.app_label}_user_change', args=[user2.pk]) + path = reverse(f"admin:{self.app_label}_user_change", args=[user2.pk]) r = self.client.get(path) self.assertEqual(r.status_code, 200) self.assertNotContains(r, f'class="readonly">{user2.username}') @@ -736,10 +736,10 @@ def test_staff_cannot_edit_org_owner(self): def _test_change(self, options): user1 = None user2 = None - group = Group.objects.get(name='Administrator') + group = Group.objects.get(name="Administrator") org = self._get_org() for key, user in options.items(): - u = self._create_user(**user.get('fields')) + u = self._create_user(**user.get("fields")) self._create_org_user(user=u, organization=org, is_admin=True) u.groups.add(group) if user1: @@ -750,87 +750,87 @@ def _test_change(self, options): user2 = user1 self.client.force_login(user1) params = user2.__dict__ - params['username'] = 'newuser1' - params['groups'] = str(group.pk) - params.pop('phone_number') - params.pop('password', None) - params.pop('_password', None) - params.pop('last_login') - params.pop('password_updated') + params["username"] = "newuser1" + params["groups"] = str(group.pk) + params.pop("phone_number") + params.pop("password", None) + params.pop("_password", None) + params.pop("last_login") + params.pop("password_updated") params.update(self.add_user_inline_params) params.update(self._additional_params_add()) params.update(self._get_user_edit_form_inline_params(user2, org)) - path = reverse(f'admin:{self.app_label}_user_change', args=[user2.pk]) + path = reverse(f"admin:{self.app_label}_user_change", args=[user2.pk]) r = self.client.post(path, params, follow=True) self.assertEqual(r.status_code, 200) - self.assertNotContains(r, 'Please correct the error below.') + self.assertNotContains(r, "Please correct the error below.") user2.refresh_from_db() - self.assertEqual(user2.username, 'newuser1') + self.assertEqual(user2.username, "newuser1") def test_staff_can_edit_staff(self): options = { - 'user1': { - 'fields': { - 'username': 'user1', - 'email': 'email1@mail.com', - 'is_staff': True, + "user1": { + "fields": { + "username": "user1", + "email": "email1@mail.com", + "is_staff": True, }, - 'is_owner': False, + "is_owner": False, }, - 'user2': { - 'fields': { - 'username': 'user2', - 'email': 'email2@mail.com', - 'is_staff': True, + "user2": { + "fields": { + "username": "user2", + "email": "email2@mail.com", + "is_staff": True, }, - 'is_owner': False, + "is_owner": False, }, } self._test_change(options) def test_org_owner_can_edit_staff(self): options = { - 'user1': { - 'fields': { - 'username': 'user1', - 'email': 'email1@mail.com', - 'is_staff': True, + "user1": { + "fields": { + "username": "user1", + "email": "email1@mail.com", + "is_staff": True, }, - 'is_owner': True, + "is_owner": True, }, - 'user2': { - 'fields': { - 'username': 'user2', - 'email': 'email2@mail.com', - 'is_staff': True, + "user2": { + "fields": { + "username": "user2", + "email": "email2@mail.com", + "is_staff": True, }, - 'is_owner': False, + "is_owner": False, }, } self._test_change(options) def test_org_owner_can_edit_org_owner(self): options = { - 'user1': { - 'fields': { - 'username': 'user1', - 'email': 'email1@mail.com', - 'is_staff': True, + "user1": { + "fields": { + "username": "user1", + "email": "email1@mail.com", + "is_staff": True, }, - 'is_owner': True, + "is_owner": True, } } self._test_change(options) def test_staff_can_edit_itself(self): options = { - 'user1': { - 'fields': { - 'username': 'user1', - 'email': 'email1@mail.com', - 'is_staff': True, + "user1": { + "fields": { + "username": "user1", + "email": "email1@mail.com", + "is_staff": True, }, - 'is_owner': False, + "is_owner": False, } } self._test_change(options) @@ -838,23 +838,23 @@ def test_staff_can_edit_itself(self): def test_admin_add_user_by_superuser(self): admin = self._create_admin() self.client.force_login(admin) - res = self.client.get(reverse(f'admin:{self.app_label}_user_add')) - self.assertContains(res, 'is_superuser') + res = self.client.get(reverse(f"admin:{self.app_label}_user_add")) + self.assertContains(res, "is_superuser") def test_admin_add_user_by_operator(self): operator = self._create_operator_with_user_permissions() self.client.force_login(operator) - res = self.client.get(reverse(f'admin:{self.app_label}_user_add')) - self.assertNotContains(res, 'is_superuser') + res = self.client.get(reverse(f"admin:{self.app_label}_user_add")) + self.assertNotContains(res, "is_superuser") def test_admin_add_user_org_required(self): admin = self._create_admin() self.client.force_login(admin) params = dict( - username='testadd', - email='test@testadd.com', - password1='tester', - password2='tester', + username="testadd", + email="test@testadd.com", + password1="tester", + password2="tester", is_staff=True, is_superuser=False, ) @@ -862,106 +862,106 @@ def test_admin_add_user_org_required(self): params.update(self._additional_params_add()) params.update( { - f'{self.app_label}_organizationuser-TOTAL_FORMS': 1, - f'{self.app_label}_organizationuser-INITIAL_FORMS': 0, - f'{self.app_label}_organizationuser-MIN_NUM_FORMS': 0, - f'{self.app_label}_organizationuser-MAX_NUM_FORMS': 1, + f"{self.app_label}_organizationuser-TOTAL_FORMS": 1, + f"{self.app_label}_organizationuser-INITIAL_FORMS": 0, + f"{self.app_label}_organizationuser-MIN_NUM_FORMS": 0, + f"{self.app_label}_organizationuser-MAX_NUM_FORMS": 1, } ) - res = self.client.post(reverse(f'admin:{self.app_label}_user_add'), params) - queryset = User.objects.filter(username='testadd') + res = self.client.post(reverse(f"admin:{self.app_label}_user_add"), params) + queryset = User.objects.filter(username="testadd") self.assertEqual(queryset.count(), 0) - self.assertContains(res, 'errors field-organization') + self.assertContains(res, "errors field-organization") def test_admin_user_add_form(self): self.client.force_login(self._get_admin()) - r = self.client.get(reverse(f'admin:{self.app_label}_user_add')) - self.assertContains(r, 'first_name') - self.assertContains(r, 'last_name') - self.assertContains(r, 'phone_number') - self.assertContains(r, 'groups') + r = self.client.get(reverse(f"admin:{self.app_label}_user_add")) + self.assertContains(r, "first_name") + self.assertContains(r, "last_name") + self.assertContains(r, "phone_number") + self.assertContains(r, "groups") def test_add_staff_without_group(self): admin = self._create_admin() self.client.force_login(admin) org = self._get_org() params = dict( - username='testadd', - email='test@testadd.com', - password1='tester', - password2='tester', + username="testadd", + email="test@testadd.com", + password1="tester", + password2="tester", is_staff=True, ) params.update(self.add_user_inline_params) params.update(self._additional_params_add()) params.update( { - f'{self.app_label}_organizationuser-TOTAL_FORMS': 1, - f'{self.app_label}_organizationuser-INITIAL_FORMS': 0, - f'{self.app_label}_organizationuser-MIN_NUM_FORMS': 0, - f'{self.app_label}_organizationuser-MAX_NUM_FORMS': 1, - f'{self.app_label}_organizationuser-0-is_admin': 'on', - f'{self.app_label}_organizationuser-0-organization': str(org.pk), + f"{self.app_label}_organizationuser-TOTAL_FORMS": 1, + f"{self.app_label}_organizationuser-INITIAL_FORMS": 0, + f"{self.app_label}_organizationuser-MIN_NUM_FORMS": 0, + f"{self.app_label}_organizationuser-MAX_NUM_FORMS": 1, + f"{self.app_label}_organizationuser-0-is_admin": "on", + f"{self.app_label}_organizationuser-0-organization": str(org.pk), } ) res = self.client.post( - reverse(f'admin:{self.app_label}_user_add'), params, follow=True + reverse(f"admin:{self.app_label}_user_add"), params, follow=True ) self.assertEqual(res.status_code, 200) self.assertContains( - res, 'A staff user must belong to a group, please select one.' + res, "A staff user must belong to a group, please select one." ) - user = User.objects.filter(username='testadd') + user = User.objects.filter(username="testadd") self.assertEqual(user.count(), 0) def test_add_staff_with_group(self): admin = self._create_admin() self.client.force_login(admin) - group = Group.objects.get(name='Administrator') + group = Group.objects.get(name="Administrator") org = self._get_org() params = dict( - username='testadd', - email='test@testadd.com', - password1='tester', - password2='tester', + username="testadd", + email="test@testadd.com", + password1="tester", + password2="tester", is_staff=True, ) params.update(self.add_user_inline_params) params.update(self._additional_params_add()) params.update( { - 'groups': str(group.pk), - f'{self.app_label}_organizationuser-TOTAL_FORMS': 1, - f'{self.app_label}_organizationuser-INITIAL_FORMS': 0, - f'{self.app_label}_organizationuser-MIN_NUM_FORMS': 0, - f'{self.app_label}_organizationuser-MAX_NUM_FORMS': 1, - f'{self.app_label}_organizationuser-0-is_admin': 'on', - f'{self.app_label}_organizationuser-0-organization': str(org.pk), + "groups": str(group.pk), + f"{self.app_label}_organizationuser-TOTAL_FORMS": 1, + f"{self.app_label}_organizationuser-INITIAL_FORMS": 0, + f"{self.app_label}_organizationuser-MIN_NUM_FORMS": 0, + f"{self.app_label}_organizationuser-MAX_NUM_FORMS": 1, + f"{self.app_label}_organizationuser-0-is_admin": "on", + f"{self.app_label}_organizationuser-0-organization": str(org.pk), } ) res = self.client.post( - reverse(f'admin:{self.app_label}_user_add'), params, follow=True + reverse(f"admin:{self.app_label}_user_add"), params, follow=True ) self.assertEqual(res.status_code, 200) - self.assertNotContains(res, 'Please correct the error below.') - user = User.objects.filter(username='testadd') + self.assertNotContains(res, "Please correct the error below.") + user = User.objects.filter(username="testadd") self.assertEqual(user.count(), 1) def test_add_user_fieldsets(self): self.client.force_login(self._get_admin()) - r = self.client.get(reverse(f'admin:{self.app_label}_user_add')) + r = self.client.get(reverse(f"admin:{self.app_label}_user_add")) self.assertEqual(r.status_code, 200) - self.assertContains(r, 'Permissions') - self.assertContains(r, 'Personal Info') + self.assertContains(r, "Permissions") + self.assertContains(r, "Personal Info") def test_admin_add_superuser_org_not_required(self): admin = self._create_admin() self.client.force_login(admin) params = dict( - username='testadd', - email='test@testadd.com', - password1='tester', - password2='tester', + username="testadd", + email="test@testadd.com", + password1="tester", + password2="tester", is_staff=True, is_superuser=True, ) @@ -969,18 +969,18 @@ def test_admin_add_superuser_org_not_required(self): params.update(self._additional_params_add()) params.update( { - f'{self.app_label}_organizationuser-TOTAL_FORMS': 1, - f'{self.app_label}_organizationuser-INITIAL_FORMS': 0, - f'{self.app_label}_organizationuser-MIN_NUM_FORMS': 0, - f'{self.app_label}_organizationuser-MAX_NUM_FORMS': 1, + f"{self.app_label}_organizationuser-TOTAL_FORMS": 1, + f"{self.app_label}_organizationuser-INITIAL_FORMS": 0, + f"{self.app_label}_organizationuser-MIN_NUM_FORMS": 0, + f"{self.app_label}_organizationuser-MAX_NUM_FORMS": 1, } ) res = self.client.post( - reverse(f'admin:{self.app_label}_user_add'), params, follow=True + reverse(f"admin:{self.app_label}_user_add"), params, follow=True ) - self.assertNotContains(res, 'errors field-organization') - self.assertNotContains(res, 'Please correct the errors below.') - queryset = User.objects.filter(username='testadd') + self.assertNotContains(res, "errors field-organization") + self.assertNotContains(res, "Please correct the errors below.") + queryset = User.objects.filter(username="testadd") self.assertEqual(queryset.count(), 1) user = queryset.first() self.assertTrue(user.is_superuser) @@ -991,7 +991,7 @@ def test_operator_change_user_permissions(self): self.client.force_login(operator) admin = self._create_admin() response = self.client.get( - reverse(f'admin:{self.app_label}_user_change', args=[admin.pk]) + reverse(f"admin:{self.app_label}_user_change", args=[admin.pk]) ) self.assertEqual(response.status_code, 302) @@ -1001,10 +1001,10 @@ def test_user_add_user(self): # removing the "add_organizationuser" permission allows # achieving more test coverage add_organizationuser = Permission.objects.get( - codename__endswith='add_organizationuser' + codename__endswith="add_organizationuser" ) admin.user_permissions.remove(add_organizationuser) - response = self.client.get(reverse(f'admin:{self.app_label}_user_add')) + response = self.client.get(reverse(f"admin:{self.app_label}_user_add")) self.assertContains(response, 'Delete') - with self.subTest('managers can not delete inline org owner'): + with self.subTest("managers can not delete inline org owner"): user1 = self._create_user( - username='change1', - password='change1', - email='email1@email.com', + username="change1", + password="change1", + email="email1@email.com", is_staff=True, ) user1.groups.set(group) @@ -1547,7 +1547,7 @@ def test_only_superuser_can_delete_inline_org_owner(self): self.assertEqual(r.status_code, 200) self.assertNotContains(r, '-DELETE">Delete') - with self.subTest('superuser can delete inline org owner'): + with self.subTest("superuser can delete inline org owner"): self.client.force_login(self._get_admin()) r = self.client.get(path) self.assertEqual(r.status_code, 200) @@ -1555,75 +1555,75 @@ def test_only_superuser_can_delete_inline_org_owner(self): def test_delete_org_user(self): self.client.force_login(self._get_admin()) - user1 = self._create_user(username='user1', email='user1@email.com') - org1 = self._create_org(name='org1') + user1 = self._create_user(username="user1", email="user1@email.com") + org1 = self._create_org(name="org1") org_user = self._create_org_user(user=user1, organization=org1, is_admin=True) - with self.subTest('test delete org user which belongs to owner'): - post_data = {'post': 'yes'} + with self.subTest("test delete org user which belongs to owner"): + post_data = {"post": "yes"} url = reverse( - f'admin:{self.app_label}_organizationuser_delete', args=[org_user.pk] + f"admin:{self.app_label}_organizationuser_delete", args=[org_user.pk] ) r = self.client.post(url, post_data, follow=True) qs = OrganizationUser.objects.filter(organization=org1, user=user1) self.assertEqual(r.status_code, 200) msg = ( - 't delete this organization user because it ' - 'belongs to an organization owner' + "t delete this organization user because it " + "belongs to an organization owner" ) self.assertContains(r, msg) self.assertEqual(qs.count(), 1) - with self.subTest('test delete org user which belongs to no owner'): - org2 = self._create_org(name='org2') + with self.subTest("test delete org user which belongs to no owner"): + org2 = self._create_org(name="org2") org_u = self._create_org_user(user=user1, organization=org2, is_admin=False) - post_data = {'post': 'yes'} + post_data = {"post": "yes"} url = reverse( - f'admin:{self.app_label}_organizationuser_delete', args=[org_u.pk] + f"admin:{self.app_label}_organizationuser_delete", args=[org_u.pk] ) r = self.client.post(url, post_data, follow=True) qs = OrganizationUser.objects.filter(organization=org2, user=user1) self.assertEqual(r.status_code, 200) - self.assertContains(r, 'was deleted successfully.') + self.assertContains(r, "was deleted successfully.") self.assertEqual(qs.count(), 0) with self.subTest("Can not delete only owner's org user with action"): post_data = { - 'action': 'delete_selected_overridden', - '_selected_action': [org_user.pk], - 'post': 'yes', + "action": "delete_selected_overridden", + "_selected_action": [org_user.pk], + "post": "yes", } - url = reverse(f'admin:{self.app_label}_organizationuser_changelist') + url = reverse(f"admin:{self.app_label}_organizationuser_changelist") # django-reversion adds ~4 queries with self.assertNumQueries(12): r = self.client.post(url, post_data, follow=True) qs = OrganizationUser.objects.filter(user=user1, organization=org1) self.assertEqual(r.status_code, 200) - self.assertContains(r, 't delete organization users which belong to owners') + self.assertContains(r, "t delete organization users which belong to owners") self.assertEqual(qs.count(), 1) - with self.subTest('delete org users with some belonging to owners'): - org2 = self._create_org(name='org2') + with self.subTest("delete org users with some belonging to owners"): + org2 = self._create_org(name="org2") org_user2 = self._create_org_user(user=user1, organization=org2) post_data = { - 'action': 'delete_selected_overridden', - '_selected_action': [org_user.pk, org_user2.pk], + "action": "delete_selected_overridden", + "_selected_action": [org_user.pk, org_user2.pk], } - url = reverse(f'admin:{self.app_label}_organizationuser_changelist') + url = reverse(f"admin:{self.app_label}_organizationuser_changelist") r = self.client.post(url, post_data, follow=True) self.assertEqual(r.status_code, 200) msg = ( - 't delete 1 organization user because ' - 'it belongs to an organization owner' + "t delete 1 organization user because " + "it belongs to an organization owner" ) self.assertContains(r, msg) - post_data.update({'post': 'yes'}) + post_data.update({"post": "yes"}) # django-reversion adds ~4 queries with self.assertNumQueries(21): r = self.client.post(url, post_data, follow=True) qs = OrganizationUser.objects.filter(pk__in=[org_user.pk, org_user2.pk]) self.assertEqual(r.status_code, 200) - self.assertContains(r, 'Successfully deleted 1 organization user.') + self.assertContains(r, "Successfully deleted 1 organization user.") self.assertEqual(qs.count(), 1) self.assertEqual(qs.first().organization, org1) @@ -1635,17 +1635,17 @@ def test_delete_selected_overridden_org_user_action_perms(self): org_user_obj = self._create_org_user( organization=org, user=self._create_user( - username='delete-user', email='delete-user@example.com' + username="delete-user", email="delete-user@example.com" ), ) self._test_action_permission( - path=reverse(f'admin:{self.app_label}_organizationuser_changelist'), - action='delete_selected_overridden', + path=reverse(f"admin:{self.app_label}_organizationuser_changelist"), + action="delete_selected_overridden", user=user, obj=org_user_obj, - message='Successfully deleted 1 organization user.', - required_perms=['delete'], - extra_payload={'post': 'yes'}, + message="Successfully deleted 1 organization user.", + required_perms=["delete"], + extra_payload={"post": "yes"}, ) @capture_any_output() @@ -1653,18 +1653,18 @@ def test_admin_add_user_with_invalid_email(self): admin = self._create_admin() self.client.force_login(admin) params = dict( - username='testmail', - email='test@invalid.com', - password1='tester', - password2='tester', + username="testmail", + email="test@invalid.com", + password1="tester", + password2="tester", ) params.update(self.add_user_inline_params) params.update(self._additional_params_add()) - with patch('allauth.account.models.EmailAddress.objects.add_email') as mocked: + with patch("allauth.account.models.EmailAddress.objects.add_email") as mocked: mocked.side_effect = smtplib.SMTPSenderRefused( - 501, '5.1.7 Bad sender address syntax', 'test_name@test_domain' + 501, "5.1.7 Bad sender address syntax", "test_name@test_domain" ) - self.client.post(reverse(f'admin:{self.app_label}_user_add'), params) + self.client.post(reverse(f"admin:{self.app_label}_user_add"), params) mocked.assert_called_once() def test_admin_menu_groups(self): @@ -1673,18 +1673,18 @@ def test_admin_menu_groups(self): admin = self._create_admin() self.client.force_login(admin) models = [ - 'user', - 'organization', - 'organizationowner', - 'organizationuser', - 'group', + "user", + "organization", + "organizationowner", + "organizationuser", + "group", ] - response = self.client.get(reverse('admin:index')) + response = self.client.get(reverse("admin:index")) for model in models: - with self.subTest(f'test menu group link for {model} model'): - url = reverse(f'admin:{self.app_label}_{model}_changelist') + with self.subTest(f"test menu group link for {model} model"): + url = reverse(f"admin:{self.app_label}_{model}_changelist") self.assertContains(response, f'class="mg-link" href="{url}"') - with self.subTest('test user and organization group is registered'): + with self.subTest("test user and organization group is registered"): self.assertContains( response, '
    Users & Organizations
    ', @@ -1700,20 +1700,20 @@ class TestBasicUsersIntegration( (designed to be inherited in other openwisp modules) """ - app_label = 'openwisp_users' + app_label = "openwisp_users" is_integration_test = True def _get_user_edit_form_inline_params(self, user, organization): params = { # email address inline - 'emailaddress_set-TOTAL_FORMS': 1, - 'emailaddress_set-INITIAL_FORMS': 1, - 'emailaddress_set-MIN_NUM_FORMS': 0, - 'emailaddress_set-MAX_NUM_FORMS': 0, - 'emailaddress_set-0-verified': True, - 'emailaddress_set-0-primary': True, - 'emailaddress_set-0-id': user.emailaddress_set.first().id, - 'emailaddress_set-0-user': str(user.pk), + "emailaddress_set-TOTAL_FORMS": 1, + "emailaddress_set-INITIAL_FORMS": 1, + "emailaddress_set-MIN_NUM_FORMS": 0, + "emailaddress_set-MAX_NUM_FORMS": 0, + "emailaddress_set-0-verified": True, + "emailaddress_set-0-primary": True, + "emailaddress_set-0-id": user.emailaddress_set.first().id, + "emailaddress_set-0-user": str(user.pk), } try: @@ -1726,18 +1726,18 @@ def _get_user_edit_form_inline_params(self, user, organization): params.update( { # organization user inline - f'{self.app_label}_organizationuser-TOTAL_FORMS': 1, - f'{self.app_label}_organizationuser-INITIAL_FORMS': 1, - f'{self.app_label}_organizationuser-MIN_NUM_FORMS': 0, - f'{self.app_label}_organizationuser-MAX_NUM_FORMS': 1000, - f'{self.app_label}_organizationuser-0-is_admin': False, - f'{self.app_label}_organizationuser-0-organization': str( + f"{self.app_label}_organizationuser-TOTAL_FORMS": 1, + f"{self.app_label}_organizationuser-INITIAL_FORMS": 1, + f"{self.app_label}_organizationuser-MIN_NUM_FORMS": 0, + f"{self.app_label}_organizationuser-MAX_NUM_FORMS": 1000, + f"{self.app_label}_organizationuser-0-is_admin": False, + f"{self.app_label}_organizationuser-0-organization": str( organization.pk ), - f'{self.app_label}_organizationuser-0-id': str( + f"{self.app_label}_organizationuser-0-id": str( organization_user.pk ), - f'{self.app_label}_organizationuser-0-user': str(user.pk), + f"{self.app_label}_organizationuser-0-user": str(user.pk), } ) return params @@ -1749,54 +1749,54 @@ def test_change_user(self): self._create_org_user(organization=org, user=user) self.client.force_login(admin) params = user.__dict__ - params['bio'] = 'Test change' - params.pop('phone_number') - params.pop('password', None) - params.pop('_password', None) - params.pop('last_login') - params.pop('password_updated') - params['birth_date'] = user.date_joined.date() + params["bio"] = "Test change" + params.pop("phone_number") + params.pop("password", None) + params.pop("_password", None) + params.pop("last_login") + params.pop("password_updated") + params["birth_date"] = user.date_joined.date() params = self._additional_params_pop(params) params.update(self._get_user_edit_form_inline_params(user, org)) - url = reverse(f'admin:{self.app_label}_user_change', args=[user.pk]) + url = reverse(f"admin:{self.app_label}_user_change", args=[user.pk]) response = self.client.post( url, params, follow=True, ) - self.assertNotContains(response, 'Please correct the error below.') + self.assertNotContains(response, "Please correct the error below.") user.refresh_from_db() - self.assertEqual(user.bio, params['bio']) - self.assertEqual(user.birth_date, params['birth_date']) - with self.subTest('test presence of fields'): + self.assertEqual(user.bio, params["bio"]) + self.assertEqual(user.birth_date, params["birth_date"]) + with self.subTest("test presence of fields"): response = self.client.get(url) - self.assertContains(response, 'id_birth_date') - self.assertContains(response, 'notes for internal usage') + self.assertContains(response, "id_birth_date") + self.assertContains(response, "notes for internal usage") def _delete_inline_org_user(self, is_admin=False): admin = self._create_admin() user = self._create_user() - org = self._create_org(name='inline-org') + org = self._create_org(name="inline-org") self._create_org_user(organization=org, user=user, is_admin=is_admin) self.client.force_login(admin) params = user.__dict__ - params.pop('phone_number') - params.pop('password', None) - params.pop('_password', None) - params.pop('last_login') - params.pop('password_updated') + params.pop("phone_number") + params.pop("password", None) + params.pop("_password", None) + params.pop("last_login") + params.pop("password_updated") params = self._additional_params_pop(params) params.update(self._get_user_edit_form_inline_params(user, org)) - params.update({f'{self.app_label}_organizationuser-0-DELETE': 'on'}) - path = reverse(f'admin:{self.app_label}_user_change', args=[user.pk]) + params.update({f"{self.app_label}_organizationuser-0-DELETE": "on"}) + path = reverse(f"admin:{self.app_label}_user_change", args=[user.pk]) r = self.client.post(path, params, follow=True) qs = OrganizationUser.objects.filter(user=user) self.assertEqual(r.status_code, 200) if is_admin: single_msg = ( - 't delete 1 organization user because it ' - 'belongs to an organization owner.' + "t delete 1 organization user because it " + "belongs to an organization owner." ) self.assertContains(r, single_msg) self.assertEqual(qs.count(), 1) @@ -1810,33 +1810,33 @@ def test_delete_inline_owner_org_user(self): self._delete_inline_org_user(is_admin=True) def test_login_page(self): - r = self.client.get(reverse('admin:login')) + r = self.client.get(reverse("admin:login")) - with self.subTest('Test forgot password link'): + with self.subTest("Test forgot password link"): self.assertContains( r, 'Forgot Password?') - self.assertContains(r, 'Email, phone number or username:') + self.assertContains(r, "Email, phone number or username:") class TestMultitenantAdmin(TestMultitenantAdminMixin, TestCase): - app_label = 'openwisp_users' + app_label = "openwisp_users" def _create_multitenancy_test_env(self): - org1 = self._create_org(name='organization1') - org2 = self._create_org(name='organization2') - org3 = self._create_org(name='organization3') - user1 = self._create_user(username='user1', email='user1j@something.com') - user12 = self._create_user(username='user12', email='user12j@something.com') - user2 = self._create_user(username='user2', email='user2j@something.com') - user22 = self._create_user(username='user22', email='user22j@something.com') + org1 = self._create_org(name="organization1") + org2 = self._create_org(name="organization2") + org3 = self._create_org(name="organization3") + user1 = self._create_user(username="user1", email="user1j@something.com") + user12 = self._create_user(username="user12", email="user12j@something.com") + user2 = self._create_user(username="user2", email="user2j@something.com") + user22 = self._create_user(username="user22", email="user22j@something.com") user23 = self._create_user( - username='user23', email='user23j@something.com', is_superuser=True + username="user23", email="user23j@something.com", is_superuser=True ) - user3 = self._create_user(username='user3', email='user3@something.com') + user3 = self._create_user(username="user3", email="user3@something.com") organization_user1 = self._create_org_user( organization=org1, user=user1, is_admin=True ) @@ -1895,151 +1895,151 @@ def _make_org_manager(self, user, org): def test_multitenancy_organization_user_queryset(self): data = self._create_multitenancy_test_env() - self._make_org_manager(data['administrator'], data['org1']) + self._make_org_manager(data["administrator"], data["org1"]) self._test_multitenant_admin( - url=reverse(f'admin:{self.app_label}_organizationuser_changelist'), + url=reverse(f"admin:{self.app_label}_organizationuser_changelist"), hidden=[ - data['organization_user2'].user.username, - data['organization_user22'].user.username, + data["organization_user2"].user.username, + data["organization_user22"].user.username, ], visible=[ - data['organization_user1'].user.username, - data['organization_user12'].user.username, - data['organization_user1o'].user.username, - data['organization_user3'].user.username, + data["organization_user1"].user.username, + data["organization_user12"].user.username, + data["organization_user1o"].user.username, + data["organization_user3"].user.username, ], administrator=True, ) def test_multitenancy_organization_owner_queryset(self): data = self._create_multitenancy_test_env() - self._make_org_manager(data['administrator'], data['org1']) + self._make_org_manager(data["administrator"], data["org1"]) self._test_multitenant_admin( - url=reverse(f'admin:{self.app_label}_organizationowner_changelist'), - hidden=[data['organization_owner2'].organization_user.user.username], - visible=[data['organization_owner1'].organization_user.user.username], + url=reverse(f"admin:{self.app_label}_organizationowner_changelist"), + hidden=[data["organization_owner2"].organization_user.user.username], + visible=[data["organization_owner1"].organization_user.user.username], administrator=True, ) def test_useradmin_specific_multitenancy_costraints(self): data = self._create_multitenancy_test_env() self._test_multitenant_admin( - url=reverse(f'admin:{self.app_label}_user_changelist'), - visible=[data['user3'], data['operator']], - hidden=[data['user2'], data['user22'], data['user1'], data['user12']], + url=reverse(f"admin:{self.app_label}_user_changelist"), + visible=[data["user3"], data["operator"]], + hidden=[data["user2"], data["user22"], data["user1"], data["user12"]], administrator=True, ) def test_multitenant_admin_manager_only(self): staff = self._create_user( - username='staff__user', email='staff@staff.org', is_staff=True + username="staff__user", email="staff@staff.org", is_staff=True ) - staff_org = self._create_org(name='staff_org') - other_org = Organization.objects.create(name='other org', slug='other-org') - admin_group = Group.objects.get(name='Administrator') + staff_org = self._create_org(name="staff_org") + other_org = Organization.objects.create(name="other org", slug="other-org") + admin_group = Group.objects.get(name="Administrator") staff.groups.add(admin_group) self._create_org_user(organization=other_org, user=staff, is_admin=False) self._create_org_user(organization=staff_org, user=staff, is_admin=True) self._login(staff.username) - user1 = self._create_user(username='user1__otherorg', email='user1@user1.org') + user1 = self._create_user(username="user1__otherorg", email="user1@user1.org") self._create_org_user(organization=other_org, user=user1, is_admin=False) self._test_multitenant_admin( - url=reverse(f'admin:{self.app_label}_organizationuser_changelist'), + url=reverse(f"admin:{self.app_label}_organizationuser_changelist"), hidden=[user1.username], visible=[staff.username], ) def test_staff_user_manager_of_multiple_orgs_bug(self): staff = self._create_user( - username='staff__user', email='staff@staff.org', is_staff=True + username="staff__user", email="staff@staff.org", is_staff=True ) - staff_org = self._create_org(name='staff_org') - other_org = Organization.objects.create(name='other org', slug='other-org') - admin_group = Group.objects.get(name='Administrator') + staff_org = self._create_org(name="staff_org") + other_org = Organization.objects.create(name="other org", slug="other-org") + admin_group = Group.objects.get(name="Administrator") staff.groups.add(admin_group) self._create_org_user(organization=other_org, user=staff, is_admin=True) self._create_org_user(organization=staff_org, user=staff, is_admin=True) self._login(staff.username) - response = self.client.get(reverse(f'admin:{self.app_label}_user_changelist')) + response = self.client.get(reverse(f"admin:{self.app_label}_user_changelist")) self.assertContains(response, staff.email, count=1) def test_class_attr_regression(self): class TestAdmin(MultitenantAdminMixin): - multitenant_parent = 'test' + multitenant_parent = "test" owner_admin = OrganizationOwnerAdmin(Organization, admin.site) test_admin = TestAdmin() - with self.subTest('multitenant_parent added to multitenant_shared_relations'): - self.assertIn('test', test_admin.multitenant_shared_relations) - with self.subTest('mutable data structure of sibling class unaffected'): - self.assertNotIn('test', owner_admin.multitenant_shared_relations) - with self.subTest('original attribute unaffected'): + with self.subTest("multitenant_parent added to multitenant_shared_relations"): + self.assertIn("test", test_admin.multitenant_shared_relations) + with self.subTest("mutable data structure of sibling class unaffected"): + self.assertNotIn("test", owner_admin.multitenant_shared_relations) + with self.subTest("original attribute unaffected"): self.assertIsNone(MultitenantAdminMixin.multitenant_shared_relations) def test_organization_user_filter(self): data = self._create_multitenancy_test_env() - self._make_org_manager(data['administrator'], data['org1']) - url = reverse('admin:ow-auto-filter') + self._make_org_manager(data["administrator"], data["org1"]) + url = reverse("admin:ow-auto-filter") payload = { - 'app_label': self.app_label, - 'model_name': 'user', - 'field_name': f'{self.app_label}_organization', + "app_label": self.app_label, + "model_name": "user", + "field_name": f"{self.app_label}_organization", } - with self.subTest('test superadmin'): + with self.subTest("test superadmin"): user = User.objects.filter(is_superuser=True, is_staff=True).first() self.client.force_login(user) response = self.client.get(url, payload) self.assertEqual(response.status_code, 200) - for option in response.json()['results']: - assert option['id'] in [ - str(id) for id in Organization.objects.values_list('id', flat=True) + for option in response.json()["results"]: + assert option["id"] in [ + str(id) for id in Organization.objects.values_list("id", flat=True) ] - with self.subTest('test non superadmin'): - user = User.objects.get(username='administrator') + with self.subTest("test non superadmin"): + user = User.objects.get(username="administrator") self.client.force_login(user) response = self.client.get(url, payload) self.assertEqual(response.status_code, 200) - for option in response.json()['results']: - assert option['id'] in [str(id) for id in user.organizations_managed] - assert option['id'] not in Organization.objects.exclude( + for option in response.json()["results"]: + assert option["id"] in [str(id) for id in user.organizations_managed] + assert option["id"] not in Organization.objects.exclude( pk__in=user.organizations_managed ) class TestUserPasswordExpiration(TestOrganizationMixin, TestCase): - @patch.object(app_settings, 'STAFF_USER_PASSWORD_EXPIRATION', 30) + @patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 30) def test_expired_password_user_redirected(self): self.client.logout() user = self._create_admin() user.password_updated = now().date() - timedelta(days=31) user.save() login_response = self.client.post( - reverse('admin:login'), - data={'username': user.username, 'password': 'tester'}, + reverse("admin:login"), + data={"username": user.username, "password": "tester"}, ) self.assertEqual(login_response.status_code, 302) - self.assertEqual(login_response.url, '/accounts/password/change/?next=/admin/') + self.assertEqual(login_response.url, "/accounts/password/change/?next=/admin/") change_password_response = self.client.get(login_response.url) self.assertEqual(change_password_response.status_code, 200) self.assertContains( change_password_response, - 'Your password has expired, please update your password.', + "Your password has expired, please update your password.", ) - @patch.object(app_settings, 'STAFF_USER_PASSWORD_EXPIRATION', 30) + @patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 30) def test_non_expired_user_django_redirection(self): self.client.logout() - redirect_url = reverse('admin:sites_site_changelist') + redirect_url = reverse("admin:sites_site_changelist") user = self._create_admin() - user.set_password('tester') + user.set_password("tester") user.save() login_response = self.client.post( - '{0}?next={1}'.format(reverse('admin:login'), redirect_url), - data={'username': user.username, 'password': 'tester'}, + "{0}?next={1}".format(reverse("admin:login"), redirect_url), + data={"username": user.username, "password": "tester"}, ) self.assertEqual(login_response.status_code, 302) self.assertEqual( @@ -2047,7 +2047,7 @@ def test_non_expired_user_django_redirection(self): redirect_url, ) - @patch.object(app_settings, 'STAFF_USER_PASSWORD_EXPIRATION', 30) + @patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 30) def test_redirection_for_expired_user_after_password_update(self): """ This test ensures that user is confined to change password @@ -2058,28 +2058,28 @@ def test_redirection_for_expired_user_after_password_update(self): user.password_updated = now().date() - timedelta(days=31) user.save() self.client.force_login(user) - site_changelist_path = reverse('admin:sites_site_changelist') + site_changelist_path = reverse("admin:sites_site_changelist") password_change_redirect_response = self.client.get(site_changelist_path) self.assertEqual(password_change_redirect_response.status_code, 302) self.assertEqual( password_change_redirect_response.url, - '{0}?{1}={2}'.format( - reverse('account_change_password'), + "{0}?{1}={2}".format( + reverse("account_change_password"), REDIRECT_FIELD_NAME, site_changelist_path, ), ) response = self.client.get(password_change_redirect_response.url) self.assertContains( - response, 'Your password has expired, please update your password.' + response, "Your password has expired, please update your password." ) change_password_response = self.client.post( password_change_redirect_response.url, data={ - 'oldpassword': 'tester', - 'password1': 'newpassword', - 'password2': 'newpassword', - 'next': site_changelist_path, + "oldpassword": "tester", + "password1": "newpassword", + "password2": "newpassword", + "next": site_changelist_path, }, follow=True, ) @@ -2088,10 +2088,10 @@ def test_redirection_for_expired_user_after_password_update(self): ( '
      \n' '
    • Password successfully changed.
    • \n' - '
    ' + "" ), html=True, ) self.assertEqual( - change_password_response.request.get('PATH_INFO'), site_changelist_path + change_password_response.request.get("PATH_INFO"), site_changelist_path ) diff --git a/openwisp_users/tests/test_api/__init__.py b/openwisp_users/tests/test_api/__init__.py index 168f25ca6..2efa2ba5f 100644 --- a/openwisp_users/tests/test_api/__init__.py +++ b/openwisp_users/tests/test_api/__init__.py @@ -5,11 +5,11 @@ class AuthenticationMixin: - def _obtain_auth_token(self, username='operator', password='tester'): - params = {'username': username, 'password': password} - url = reverse('users:user_auth_token') + def _obtain_auth_token(self, username="operator", password="tester"): + params = {"username": username, "password": password} + url = reverse("users:user_auth_token") response = self.client.post(url, params) - return response.data['token'] + return response.data["token"] class APITestCase(TestMultitenantAdminMixin, AuthenticationMixin, TestCase): diff --git a/openwisp_users/tests/test_api/test_api.py b/openwisp_users/tests/test_api/test_api.py index aeb97e7ff..109668c37 100644 --- a/openwisp_users/tests/test_api/test_api.py +++ b/openwisp_users/tests/test_api/test_api.py @@ -12,10 +12,10 @@ from ..utils import TestOrganizationMixin -Organization = load_model('openwisp_users', 'Organization') +Organization = load_model("openwisp_users", "Organization") User = get_user_model() -Group = load_model('openwisp_users', 'Group') -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') +Group = load_model("openwisp_users", "Group") +OrganizationUser = load_model("openwisp_users", "OrganizationUser") class TestUsersApi( @@ -25,239 +25,239 @@ class TestUsersApi( ): def setUp(self): user = get_user_model().objects.create_superuser( - username='administrator', password='admin', email='test@test.org' + username="administrator", password="admin", email="test@test.org" ) self.client.force_login(user) # Tests for Organization Model API endpoints def test_organization_list_api(self): - path = reverse('users:organization_list') + path = reverse("users:organization_list") with self.assertNumQueries(3): r = self.client.get(path) self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['count'], 1) + self.assertEqual(r.data["count"], 1) def test_organization_list_nonsuperuser_api(self): user = self._create_user() - view_perm = Permission.objects.filter(codename='view_organization') + view_perm = Permission.objects.filter(codename="view_organization") user.user_permissions.add(*view_perm) self.client.force_login(user) - path = reverse('users:organization_list') + path = reverse("users:organization_list") with self.assertNumQueries(4): r = self.client.get(path) self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['count'], 0) + self.assertEqual(r.data["count"], 0) self.assertEqual(Organization.objects.count(), 1) def test_organization_post_api(self): - path = reverse('users:organization_list') - data = {'name': 'test-org', 'slug': 'test-org'} + path = reverse("users:organization_list") + data = {"name": "test-org", "slug": "test-org"} with self.assertNumQueries(6): - r = self.client.post(path, data, content_type='application/json') + r = self.client.post(path, data, content_type="application/json") self.assertEqual(r.status_code, 201) self.assertEqual(Organization.objects.count(), 2) def test_organization_detail_api(self): org1 = self._get_org() - path = reverse('users:organization_detail', args=(org1.pk,)) + path = reverse("users:organization_detail", args=(org1.pk,)) with self.assertNumQueries(3): r = self.client.get(path) self.assertEqual(r.status_code, 200) def test_organization_detail_nonsuperuser_api(self): user = self._create_user() - view_perm = Permission.objects.filter(codename='view_organization') + view_perm = Permission.objects.filter(codename="view_organization") user.user_permissions.add(*view_perm) self.client.force_login(user) org1 = self._get_org() - path = reverse('users:organization_detail', args=(org1.pk,)) + path = reverse("users:organization_detail", args=(org1.pk,)) with self.assertNumQueries(4): r = self.client.get(path) self.assertEqual(r.status_code, 404) def test_organization_put_api(self): org1 = self._get_org() - self.assertEqual(org1.name, 'test org') - self.assertEqual(org1.description, '') - path = reverse('users:organization_detail', args=(org1.pk,)) + self.assertEqual(org1.name, "test org") + self.assertEqual(org1.description, "") + path = reverse("users:organization_detail", args=(org1.pk,)) data = { - 'name': 'test org change', - 'is_active': False, - 'slug': 'test-org-change', - 'description': 'testing PUT', - 'email': 'testorg@test.com', - 'url': '', + "name": "test org change", + "is_active": False, + "slug": "test-org-change", + "description": "testing PUT", + "email": "testorg@test.com", + "url": "", } with self.assertNumQueries(8): - r = self.client.put(path, data, content_type='application/json') + r = self.client.put(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['name'], 'test org change') - self.assertEqual(r.data['description'], 'testing PUT') + self.assertEqual(r.data["name"], "test org change") + self.assertEqual(r.data["description"], "testing PUT") def test_organization_patch_api(self): org1 = self._get_org() - self.assertEqual(org1.name, 'test org') - path = reverse('users:organization_detail', args=(org1.pk,)) + self.assertEqual(org1.name, "test org") + path = reverse("users:organization_detail", args=(org1.pk,)) data = { - 'name': 'test org change', + "name": "test org change", } with self.assertNumQueries(6): - r = self.client.patch(path, data, content_type='application/json') + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['name'], 'test org change') + self.assertEqual(r.data["name"], "test org change") def test_create_organization_owner_api(self): - user1 = self._create_user(username='user1', email='user1@email.com') - org1 = self._create_org(name='org1') + user1 = self._create_user(username="user1", email="user1@email.com") + org1 = self._create_org(name="org1") org1_user1 = self._create_org_user(user=user1, organization=org1) - path = reverse('users:organization_detail', args=(org1.pk,)) - data = {'owner': {'organization_user': org1_user1.pk}} + path = reverse("users:organization_detail", args=(org1.pk,)) + data = {"owner": {"organization_user": org1_user1.pk}} with self.assertNumQueries(18): - r = self.client.patch(path, data, content_type='application/json') + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['owner']['organization_user'], org1_user1.pk) + self.assertEqual(r.data["owner"]["organization_user"], org1_user1.pk) def test_remove_organization_owner_api(self): - user1 = self._create_user(username='user1', email='user1@email.com') - org1 = self._create_org(name='org1') + user1 = self._create_user(username="user1", email="user1@email.com") + org1 = self._create_org(name="org1") org1_user1 = self._create_org_user(user=user1, organization=org1) self._create_org_owner(organization_user=org1_user1, organization=org1) - path = reverse('users:organization_detail', args=(org1.pk,)) - data = {'owner': {'organization_user': ''}} + path = reverse("users:organization_detail", args=(org1.pk,)) + data = {"owner": {"organization_user": ""}} with self.assertNumQueries(12): - r = self.client.patch(path, data, content_type='application/json') + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['owner'], None) + self.assertEqual(r.data["owner"], None) def test_organization_delete_api(self): - org1 = self._create_org(name='test org 2') + org1 = self._create_org(name="test org 2") self.assertEqual(Organization.objects.count(), 2) - path = reverse('users:organization_detail', args=(org1.pk,)) + path = reverse("users:organization_detail", args=(org1.pk,)) r = self.client.delete(path) self.assertEqual(r.status_code, 204) self.assertEqual(Organization.objects.count(), 1) def test_get_organization_for_org_manager(self): - user1 = self._create_user(username='user1', email='user1@email.com') - org1 = self._create_org(name='org1') + user1 = self._create_user(username="user1", email="user1@email.com") + org1 = self._create_org(name="org1") self._create_org_user(user=user1, organization=org1, is_admin=True) - view_perm = Permission.objects.filter(codename='view_organization') + view_perm = Permission.objects.filter(codename="view_organization") user1.user_permissions.add(*view_perm) self.client.force_login(user1) - with self.subTest('Organization List'): - path = reverse('users:organization_list') + with self.subTest("Organization List"): + path = reverse("users:organization_list") with self.assertNumQueries(5): r = self.client.get(path) self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['count'], 1) + self.assertEqual(r.data["count"], 1) - with self.subTest('Organization Detail'): - path = reverse('users:organization_detail', args=(org1.pk,)) + with self.subTest("Organization Detail"): + path = reverse("users:organization_detail", args=(org1.pk,)) with self.assertNumQueries(5): r = self.client.get(path) self.assertEqual(r.status_code, 200) def test_change_organizationowner_for_org(self): - user1 = self._create_user(username='user1', email='user1@email.com') - user2 = self._create_user(username='user2', email='user2@email.com') - org1 = self._create_org(name='org1') + user1 = self._create_user(username="user1", email="user1@email.com") + user2 = self._create_user(username="user2", email="user2@email.com") + org1 = self._create_org(name="org1") org1_user1 = self._create_org_user(user=user1, organization=org1) org1_user2 = self._create_org_user(user=user2, organization=org1) self._create_org_owner(organization_user=org1_user1, organization=org1) self.assertEqual(org1.owner.organization_user.id, org1_user1.id) - path = reverse('users:organization_detail', args=(org1.pk,)) - data = {'owner': {'organization_user': org1_user2.id}} + path = reverse("users:organization_detail", args=(org1.pk,)) + data = {"owner": {"organization_user": org1_user2.id}} with self.assertNumQueries(27): - r = self.client.patch(path, data, content_type='application/json') + r = self.client.patch(path, data, content_type="application/json") org1.refresh_from_db() self.assertEqual(org1.owner.organization_user.id, org1_user2.id) self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['owner']['organization_user'], org1_user2.id) + self.assertEqual(r.data["owner"]["organization_user"], org1_user2.id) def test_orguser_filter_for_organization_detail(self): - user1 = self._create_user(username='user1', email='user1@email.com') - user2 = self._create_user(username='user2', email='user2@email.com') - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') + user1 = self._create_user(username="user1", email="user1@email.com") + user2 = self._create_user(username="user2", email="user2@email.com") + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") self._create_org_user(user=user1, organization=org1, is_admin=True) self._create_org_user(user=user2, organization=org2) - change_perm = Permission.objects.filter(codename='change_organization') + change_perm = Permission.objects.filter(codename="change_organization") user1.user_permissions.add(*change_perm) self.client.force_login(user1) - path = reverse('users:organization_detail', args=(org1.pk,)) + path = reverse("users:organization_detail", args=(org1.pk,)) with self.assertNumQueries(6): - r = self.client.get(path, {'format': 'api'}) + r = self.client.get(path, {"format": "api"}) self.assertEqual(r.status_code, 200) - self.assertContains(r, 'user1') - self.assertNotContains(r, 'user2') + self.assertContains(r, "user1") + self.assertNotContains(r, "user2") # Tests for Group Model API endpoints def test_get_group_list_403(self): - user = self._create_user(username='user1', email='user1@email.com') + user = self._create_user(username="user1", email="user1@email.com") self.client.force_login(user) - path = reverse('users:group_list') + path = reverse("users:group_list") with self.assertNumQueries(3): r = self.client.get(path) self.assertEqual(r.status_code, 403) def test_get_group_list_api(self): - path = reverse('users:group_list') + path = reverse("users:group_list") with self.assertNumQueries(5): r = self.client.get(path) self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['count'], 2) + self.assertEqual(r.data["count"], 2) def test_create_group_list_api(self): - path = reverse('users:group_list') - data = {'name': 'test-group', 'permissions': []} - r = self.client.post(path, data, content_type='application/json') + path = reverse("users:group_list") + data = {"name": "test-group", "permissions": []} + r = self.client.post(path, data, content_type="application/json") self.assertEqual(r.status_code, 201) - self.assertEqual(r.data.pop('id'), 3) + self.assertEqual(r.data.pop("id"), 3) self.assertEqual(r.data, data) def test_get_group_detail_api(self): - path = reverse('users:group_detail', args='1') + path = reverse("users:group_detail", args="1") with self.assertNumQueries(4): r = self.client.get(path) self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['id'], 1) - self.assertEqual(r.data['name'], 'Operator') - self.assertIn('Can view organization', r.data['permissions'][0]) + self.assertEqual(r.data["id"], 1) + self.assertEqual(r.data["name"], "Operator") + self.assertIn("Can view organization", r.data["permissions"][0]) def test_put_group_detail_api(self): - path = reverse('users:group_detail', args='1') - data = {'name': 'test-Operator', 'permissions': []} - r = self.client.put(path, data, content_type='application/json') + path = reverse("users:group_detail", args="1") + data = {"name": "test-Operator", "permissions": []} + r = self.client.put(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['id'], 1) - self.assertEqual(r.data['name'], 'test-Operator') + self.assertEqual(r.data["id"], 1) + self.assertEqual(r.data["name"], "test-Operator") def test_patch_group_detail_api(self): - path = reverse('users:group_detail', args='1') - data = {'permissions': [1]} - r = self.client.patch(path, data, content_type='application/json') + path = reverse("users:group_detail", args="1") + data = {"permissions": [1]} + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) self.assertEqual( - r.data['permissions'], ['1: emailaddress | Can add email address'] + r.data["permissions"], ["1: emailaddress | Can add email address"] ) def test_patch_group_detail_assign_permission_api(self): - path = reverse('users:group_detail', args='1') + path = reverse("users:group_detail", args="1") grp = Group.objects.get(id=1) - self.assertEqual(grp.permissions.values_list('codename', flat=True).count(), 1) + self.assertEqual(grp.permissions.values_list("codename", flat=True).count(), 1) data = { "permissions": [ "2: emailaddress | Can change email address", "3: emailaddress | Can delete email address", ] } - r = self.client.patch(path, data, content_type='application/json') + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(grp.permissions.values_list('codename', flat=True).count(), 2) + self.assertEqual(grp.permissions.values_list("codename", flat=True).count(), 2) def test_delete_group_detail_api(self): - path = reverse('users:group_detail', args='1') + path = reverse("users:group_detail", args="1") r = self.client.delete(path) self.assertEqual(r.status_code, 204) self.assertIsNone(r.data) @@ -266,232 +266,232 @@ def test_delete_group_detail_api(self): def test_with_wrong_password(self): user1 = self._create_user(is_staff=True) self.client.force_login(user1) - path = reverse('users:change_password', args=(user1.pk,)) - data = {'current_password': 'wrong', 'new_password': 'super1234'} + path = reverse("users:change_password", args=(user1.pk,)) + data = {"current_password": "wrong", "new_password": "super1234"} with self.assertNumQueries(5): - r = self.client.put(path, data, content_type='application/json') + r = self.client.put(path, data, content_type="application/json") self.assertEqual(r.status_code, 400) self.assertEqual( - r.data['current_password'][0][:], - 'Your old password was entered incorrectly. Please enter it again.', + r.data["current_password"][0][:], + "Your old password was entered incorrectly. Please enter it again.", ) def test_old_password_with_empty_new_password(self): user = self._get_user() - path = reverse('users:change_password', args=(user.pk,)) - data = {'old_password': 'tester', 'new_password': ''} + path = reverse("users:change_password", args=(user.pk,)) + data = {"old_password": "tester", "new_password": ""} with self.assertNumQueries(4): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.assertEqual(response.status_code, 400) def test_change_password_of_superuser_by_superuser(self): client = auth.get_user(self.client) - path = reverse('users:change_password', args=(client.pk,)) + path = reverse("users:change_password", args=(client.pk,)) data = { - 'current_password': 'admin', - 'new_password': 'super1234', - 'confirm_password': 'super1234', + "current_password": "admin", + "new_password": "super1234", + "confirm_password": "super1234", } with self.assertNumQueries(5): - r = self.client.put(path, data, content_type='application/json') + r = self.client.put(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['status'], 'Success') - self.assertEqual(r.data['message'], 'Password updated successfully') + self.assertEqual(r.data["status"], "Success") + self.assertEqual(r.data["message"], "Password updated successfully") def test_change_password_of_other_user_by_superuser(self): user1 = self._create_user( - username='user1233', password='tester123', email='user@test.com' + username="user1233", password="tester123", email="user@test.com" ) data = { - 'current_password': 'wrong', - 'new_password': 'change123', - 'confirm_password': 'change123', + "current_password": "wrong", + "new_password": "change123", + "confirm_password": "change123", } - path = reverse('users:change_password', args=(user1.pk,)) + path = reverse("users:change_password", args=(user1.pk,)) with self.assertNumQueries(5): - r = self.client.put(path, data, content_type='application/json') + r = self.client.put(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) def test_change_password_of_different_org_user(self): - user2 = self._create_user(username='user2', email='user2@mail.com') - org1 = self._create_org(name='org1') + user2 = self._create_user(username="user2", email="user2@mail.com") + org1 = self._create_org(name="org1") org1_manager = self._create_user( - username='org1_manager', password='test123', email='org1_manager@test.com' + username="org1_manager", password="test123", email="org1_manager@test.com" ) self._create_org_user(organization=org1, user=org1_manager, is_admin=True) - administrator = Group.objects.get(name='Administrator') + administrator = Group.objects.get(name="Administrator") org1_manager.groups.add(administrator) self.client.force_login(org1_manager) - path = reverse('users:change_password', args=(user2.pk,)) - data = {'old_password': 'admin', 'new_password': 'super1234'} + path = reverse("users:change_password", args=(user2.pk,)) + data = {"old_password": "admin", "new_password": "super1234"} with self.assertNumQueries(7): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.assertEqual(response.status_code, 404) def test_change_password_with_wrong_confirm_password(self): user1 = self._create_user( - username='user1233', password='tester123', email='user@test.com' + username="user1233", password="tester123", email="user@test.com" ) data = { - 'current_password': '', - 'new_password': 'change123', - 'confirm_password': 'change321', + "current_password": "", + "new_password": "change123", + "confirm_password": "change321", } - path = reverse('users:change_password', args=(user1.pk,)) + path = reverse("users:change_password", args=(user1.pk,)) with self.assertNumQueries(4): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.assertEqual(response.status_code, 400) self.assertEqual( - response.data['confirm_password'][0], - 'The two password fields didn’t match.', + response.data["confirm_password"][0], + "The two password fields didn’t match.", ) def test_change_password_with_same_old_password(self): client = auth.get_user(self.client) - path = reverse('users:change_password', args=(client.pk,)) + path = reverse("users:change_password", args=(client.pk,)) data = { - 'current_password': 'admin', - 'new_password': 'admin', - 'confirm_password': 'admin', + "current_password": "admin", + "new_password": "admin", + "confirm_password": "admin", } with self.assertNumQueries(4): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.assertEqual(response.status_code, 400) self.assertEqual( - str(response.data['new_password'][0]), - 'New password cannot be the same as your old password.', + str(response.data["new_password"][0]), + "New password cannot be the same as your old password.", ) def test_change_password_org_manager(self): # Org managers should be able to update # passwords of his org. users - org1 = self._create_org(name='org1') + org1 = self._create_org(name="org1") org1_manager = self._create_user( - username='org1_manager', password='test123', email='org1_manager@test.com' + username="org1_manager", password="test123", email="org1_manager@test.com" ) self._create_org_user(organization=org1, user=org1_manager, is_admin=True) - administrator = Group.objects.get(name='Administrator') + administrator = Group.objects.get(name="Administrator") org1_manager.groups.add(administrator) org1_user = self._create_user( - username='org1_user', - password='test321', - email='org1_user@test.com', + username="org1_user", + password="test321", + email="org1_user@test.com", is_staff=True, ) self._create_org_user(organization=org1, user=org1_user) org1_user.groups.add(administrator) - with self.subTest('Change password of org manager by manager'): + with self.subTest("Change password of org manager by manager"): self.client.force_login(org1_manager) - path = reverse('users:change_password', args=(org1_manager.pk,)) + path = reverse("users:change_password", args=(org1_manager.pk,)) data = { - 'current_password': 'test123', - 'new_password': 'test1234', - 'confirm_password': 'test1234', + "current_password": "test123", + "new_password": "test1234", + "confirm_password": "test1234", } with self.assertNumQueries(5): - r = self.client.put(path, data, content_type='application/json') + r = self.client.put(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['status'], 'Success') - self.assertEqual(r.data['message'], 'Password updated successfully') + self.assertEqual(r.data["status"], "Success") + self.assertEqual(r.data["message"], "Password updated successfully") - with self.subTest('Change password of org user by org manager'): + with self.subTest("Change password of org user by org manager"): org1_manager.refresh_from_db() self.client.force_login(org1_manager) - path = reverse('users:change_password', args=(org1_user.pk,)) + path = reverse("users:change_password", args=(org1_user.pk,)) data = { - 'current_password': 'test321', - 'new_password': 'test1234', - 'confirm_password': 'test1234', + "current_password": "test321", + "new_password": "test1234", + "confirm_password": "test1234", } with self.assertNumQueries(8): - r = self.client.put(path, data, content_type='application/json') + r = self.client.put(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['status'], 'Success') - self.assertEqual(r.data['message'], 'Password updated successfully') + self.assertEqual(r.data["status"], "Success") + self.assertEqual(r.data["message"], "Password updated successfully") - with self.subTest('change password of org user by itself'): + with self.subTest("change password of org user by itself"): org1_user.refresh_from_db() self.client.force_login(org1_user) - path = reverse('users:change_password', args=(org1_user.pk,)) + path = reverse("users:change_password", args=(org1_user.pk,)) data = { - 'current_password': 'test1234', - 'new_password': 'test1342', - 'confirm_password': 'test1342', + "current_password": "test1234", + "new_password": "test1342", + "confirm_password": "test1342", } with self.assertNumQueries(5): - r = self.client.put(path, data, content_type='application/json') + r = self.client.put(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['status'], 'Success') - self.assertEqual(r.data['message'], 'Password updated successfully') + self.assertEqual(r.data["status"], "Success") + self.assertEqual(r.data["message"], "Password updated successfully") # Tests for users email update endpoints def test_get_email_list_api(self): - user1 = self._create_user(username='user1', email='user1@email.com') - path = reverse('users:email_list', args=(user1.pk,)) + user1 = self._create_user(username="user1", email="user1@email.com") + path = reverse("users:email_list", args=(user1.pk,)) with self.assertNumQueries(5): response = self.client.get(path) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['results'][0].get('email'), 'user1@email.com') + self.assertEqual(response.data["results"][0].get("email"), "user1@email.com") def test_post_email_list_api(self): - user1 = self._create_user(username='user1', email='user1@email.com') + user1 = self._create_user(username="user1", email="user1@email.com") self.assertEqual(EmailAddress.objects.filter(user=user1).count(), 1) - path = reverse('users:email_list', args=(user1.pk,)) - data = {'email': 'newemail@test.com'} + path = reverse("users:email_list", args=(user1.pk,)) + data = {"email": "newemail@test.com"} expected_queries = 9 if django.VERSION < (5, 2) else 13 with self.assertNumQueries(expected_queries): - response = self.client.post(path, data, content_type='application/json') + response = self.client.post(path, data, content_type="application/json") self.assertEqual(response.status_code, 201) - self.assertEqual(response.data['email'], 'newemail@test.com') + self.assertEqual(response.data["email"], "newemail@test.com") self.assertEqual(EmailAddress.objects.filter(user=user1).count(), 2) def test_get_email_list_multitenancy_api(self): - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') - org1_user = self._create_user(username='org1user', email='org1user@mail.om') + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") + org1_user = self._create_user(username="org1user", email="org1user@mail.om") self._create_org_user(user=org1_user, organization=org1, is_admin=True) - email_perm = Permission.objects.filter(codename__endswith='emailaddress') + email_perm = Permission.objects.filter(codename__endswith="emailaddress") org1_user.user_permissions.add(*email_perm) - org2_user = self._create_user(username='org2user', email='org2user@mail.om') + org2_user = self._create_user(username="org2user", email="org2user@mail.om") self._create_org_user(user=org2_user, organization=org2) self.client.force_login(org1_user) - path = reverse('users:email_list', args=(org2_user.pk,)) + path = reverse("users:email_list", args=(org2_user.pk,)) with self.assertNumQueries(4): response = self.client.get(path) self.assertEqual(response.status_code, 404) def test_put_email_update_api(self): - user1 = self._create_user(username='user2', email='user2@email.com') + user1 = self._create_user(username="user2", email="user2@email.com") self.assertEqual(EmailAddress.objects.filter(user=user1).count(), 1) email_id = EmailAddress.objects.get(user=user1).id - path = reverse('users:email_update', args=(user1.pk, email_id)) - data = {'email': 'emailchange@test.com', 'primary': True} + path = reverse("users:email_update", args=(user1.pk, email_id)) + data = {"email": "emailchange@test.com", "primary": True} expected_queries = 11 if django.VERSION < (5, 2) else 15 with self.assertNumQueries(expected_queries): - response = self.client.put(path, data, content_type='application/json') + response = self.client.put(path, data, content_type="application/json") self.assertEqual(response.status_code, 200) - self.assertEqual(response.data['email'], 'emailchange@test.com') + self.assertEqual(response.data["email"], "emailchange@test.com") def test_patch_email_update_api(self): - user1 = self._create_user(username='user1', email='user1@email.com') + user1 = self._create_user(username="user1", email="user1@email.com") email_id = EmailAddress.objects.get(user=user1).id - path = reverse('users:email_update', args=(user1.pk, email_id)) - data = {'email': 'changemail@test.com'} + path = reverse("users:email_update", args=(user1.pk, email_id)) + data = {"email": "changemail@test.com"} expected_queries = 11 if django.VERSION < (5, 2) else 15 with self.assertNumQueries(expected_queries): - response = self.client.patch(path, data, content_type='application/json') + response = self.client.patch(path, data, content_type="application/json") self.assertEqual(response.status_code, 200) self.assertEqual( - EmailAddress.objects.get(user=user1).email, 'changemail@test.com' + EmailAddress.objects.get(user=user1).email, "changemail@test.com" ) def test_delete_email_api(self): - user1 = self._create_user(username='user1', email='user1@email.com') + user1 = self._create_user(username="user1", email="user1@email.com") self.assertEqual(EmailAddress.objects.filter(user=user1).count(), 1) email_id = EmailAddress.objects.get(user=user1).id - path = reverse('users:email_update', args=(user1.pk, email_id)) + path = reverse("users:email_update", args=(user1.pk, email_id)) with self.assertNumQueries(6): response = self.client.delete(path) self.assertEqual(response.status_code, 204) @@ -499,32 +499,32 @@ def test_delete_email_api(self): # Tests for superuser's User API endpoints def test_get_user_list_api(self): - path = reverse('users:user_list') + path = reverse("users:user_list") with self.assertNumQueries(5): r = self.client.get(path) self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['count'], 1) + self.assertEqual(r.data["count"], 1) def test_create_user_list_api(self): - with self.subTest('create user, standard case'): + with self.subTest("create user, standard case"): mail_sent = len(mail.outbox) self.assertEqual(User.objects.count(), 1) - path = reverse('users:user_list') + path = reverse("users:user_list") data = { - 'username': 'tester', - 'email': 'tester@test.com', - 'password': 'password123', + "username": "tester", + "email": "tester@test.com", + "password": "password123", } - r = self.client.post(path, data, content_type='application/json') + r = self.client.post(path, data, content_type="application/json") self.assertEqual(r.status_code, 201) self.assertEqual(User.objects.count(), 2) - self.assertEqual(r.data['groups'], []) - self.assertEqual(r.data['organization_users'], []) - self.assertEqual(r.data['username'], 'tester') - self.assertEqual(r.data['email'], 'tester@test.com') - self.assertEqual(r.data['is_active'], True) + self.assertEqual(r.data["groups"], []) + self.assertEqual(r.data["organization_users"], []) + self.assertEqual(r.data["username"], "tester") + self.assertEqual(r.data["email"], "tester@test.com") + self.assertEqual(r.data["is_active"], True) # ensure email address object is created but not verified - user = User.objects.filter(email=data['email']).first() + user = User.objects.filter(email=data["email"]).first() self.assertIsNotNone(user) self.assertEqual(user.emailaddress_set.count(), 1) email = user.emailaddress_set.first() @@ -533,13 +533,13 @@ def test_create_user_list_api(self): # ensure the email verification link is sent self.assertEqual(len(mail.outbox), mail_sent + 1) - with self.subTest('create user and flag email as verified'): + with self.subTest("create user and flag email as verified"): mail_sent = len(mail.outbox) - User.objects.filter(email=data['email']).delete() - data['email_verified'] = True - r = self.client.post(path, data, content_type='application/json') + User.objects.filter(email=data["email"]).delete() + data["email_verified"] = True + r = self.client.post(path, data, content_type="application/json") self.assertEqual(r.status_code, 201) - user = User.objects.filter(email=data['email']).first() + user = User.objects.filter(email=data["email"]).first() self.assertIsNotNone(user) self.assertEqual(user.emailaddress_set.count(), 1) email = user.emailaddress_set.first() @@ -549,13 +549,13 @@ def test_create_user_list_api(self): self.assertEqual(len(mail.outbox), mail_sent) def test_post_with_empty_form_api_400(self): - path = reverse('users:user_list') + path = reverse("users:user_list") with self.assertNumQueries(1): - r = self.client.post(path, {}, content_type='application/json') + r = self.client.post(path, {}, content_type="application/json") self.assertEqual(r.status_code, 400) def test_create_user_with_group_org_user_api(self): - path = reverse('users:user_list') + path = reverse("users:user_list") org1 = self._get_org() data = { "username": "tester", @@ -564,173 +564,173 @@ def test_create_user_with_group_org_user_api(self): "groups": [1], "organization_users": {"is_admin": False, "organization": org1.pk}, } - r = self.client.post(path, data, content_type='application/json') + r = self.client.post(path, data, content_type="application/json") self.assertEqual(r.status_code, 201) def test_post_with_no_email(self): - path = reverse('users:user_list') - data = {'username': '', 'email': '', 'password': ''} + path = reverse("users:user_list") + data = {"username": "", "email": "", "password": ""} with self.assertNumQueries(1): - r = self.client.post(path, data, content_type='application/json') + r = self.client.post(path, data, content_type="application/json") self.assertEqual(r.status_code, 400) def test_user_detail_no_org_user_api(self): user = self._get_user() - path = reverse('users:user_detail', args=(user.pk,)) - data = {'organization_users': []} + path = reverse("users:user_detail", args=(user.pk,)) + data = {"organization_users": []} with self.assertNumQueries(9): - r = self.client.patch(path, data, content_type='application/json') + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) def test_get_user_detail_api(self): user = self._get_user() - path = reverse('users:user_detail', args=(user.pk,)) + path = reverse("users:user_detail", args=(user.pk,)) with self.assertNumQueries(5): r = self.client.get(path) self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['id'], str(user.id)) - self.assertEqual(r.data['email'], 'test@tester.com') + self.assertEqual(r.data["id"], str(user.id)) + self.assertEqual(r.data["email"], "test@tester.com") def test_put_user_detail_api(self): user = self._get_user() org1 = self._get_org() - path = reverse('users:user_detail', args=(user.pk,)) + path = reverse("users:user_detail", args=(user.pk,)) data = { - 'username': 'tester', - 'first_name': 'Tester', - 'last_name': 'Tester', - 'email': 'test@tester.com', - 'bio': '', - 'url': '', - 'company': '', - 'location': '', - 'phone_number': None, - 'birth_date': '1987-03-23', - 'notes': '', - 'is_active': True, - 'is_staff': False, - 'is_superuser': False, - 'groups': [], - 'user_permissions': [], - 'organization_users': [{'is_admin': False, 'organization': org1.pk}], + "username": "tester", + "first_name": "Tester", + "last_name": "Tester", + "email": "test@tester.com", + "bio": "", + "url": "", + "company": "", + "location": "", + "phone_number": None, + "birth_date": "1987-03-23", + "notes": "", + "is_active": True, + "is_staff": False, + "is_superuser": False, + "groups": [], + "user_permissions": [], + "organization_users": [{"is_admin": False, "organization": org1.pk}], } self.assertEqual(OrganizationUser.objects.count(), 0) - r = self.client.put(path, data, content_type='application/json') + r = self.client.put(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) self.assertEqual(OrganizationUser.objects.count(), 1) - self.assertEqual(r.data['organization_users'][0]['organization'], org1.pk) + self.assertEqual(r.data["organization_users"][0]["organization"], org1.pk) def test_patch_user_detail_api(self): user = self._get_user() - path = reverse('users:user_detail', args=(user.pk,)) - data = {'username': 'changetestuser'} + path = reverse("users:user_detail", args=(user.pk,)) + data = {"username": "changetestuser"} with self.assertNumQueries(10): - r = self.client.patch(path, data, content_type='application/json') + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['username'], 'changetestuser') + self.assertEqual(r.data["username"], "changetestuser") def test_remove_org_user_api(self): - user1 = self._create_user(username='user1', email='user1@email.com') - org1 = self._create_org(name='org1') + user1 = self._create_user(username="user1", email="user1@email.com") + org1 = self._create_org(name="org1") self._create_org_user(user=user1, organization=org1) - path = reverse('users:user_detail', args=(user1.pk,)) - data = {'organization_users': [{'is_admin': False, 'organization': org1.pk}]} + path = reverse("users:user_detail", args=(user1.pk,)) + data = {"organization_users": [{"is_admin": False, "organization": org1.pk}]} with self.assertNumQueries(17): - r = self.client.patch(path, data, content_type='application/json') + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['organization_users'], []) + self.assertEqual(r.data["organization_users"], []) def test_make_user_org_admin_api(self): - user1 = self._create_user(username='user1', email='user1@email.com') - org1 = self._create_org(name='org1') + user1 = self._create_user(username="user1", email="user1@email.com") + org1 = self._create_org(name="org1") self._create_org_user(user=user1, organization=org1) - path = reverse('users:user_detail', args=(user1.pk,)) - data = {'organization_users': [{'is_admin': True, 'organization': org1.pk}]} - r = self.client.patch(path, data, content_type='application/json') + path = reverse("users:user_detail", args=(user1.pk,)) + data = {"organization_users": [{"is_admin": True, "organization": org1.pk}]} + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertTrue(r.data['organization_users'][0]['is_admin']) + self.assertTrue(r.data["organization_users"][0]["is_admin"]) def test_assign_user_to_groups_api(self): user = self._get_user() self.assertEqual(user.groups.count(), 0) - path = reverse('users:user_detail', args=(user.pk,)) - data = {'groups': [1]} + path = reverse("users:user_detail", args=(user.pk,)) + data = {"groups": [1]} with self.assertNumQueries(13): - r = self.client.patch(path, data, content_type='application/json') + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) self.assertEqual(user.groups.count(), 1) - self.assertEqual(r.data['groups'], [1]) + self.assertEqual(r.data["groups"], [1]) def test_assign_permission_to_user_api(self): user = self._get_user() self.assertEqual(user.get_user_permissions(), set()) self.assertEqual(user.user_permissions.count(), 0) - path = reverse('users:user_detail', args=(user.pk,)) - data = {'user_permissions': [1, 2]} + path = reverse("users:user_detail", args=(user.pk,)) + data = {"user_permissions": [1, 2]} with self.assertNumQueries(14): - r = self.client.patch(path, data, content_type='application/json') + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) self.assertEqual(user.user_permissions.count(), 2) - self.assertEqual(r.data['user_permissions'], [1, 2]) + self.assertEqual(r.data["user_permissions"], [1, 2]) def test_delete_user_api(self): user = self._get_user() - path = reverse('users:user_detail', args=(user.pk,)) + path = reverse("users:user_detail", args=(user.pk,)) r = self.client.delete(path) self.assertEqual(r.status_code, 204) def test_user_list_for_nonsuperuser_api(self): - org1 = self._create_org(name='org1') + org1 = self._create_org(name="org1") org1_manager = self._create_user( - username='org1_manager', password='test123', email='org1_manager@test.com' + username="org1_manager", password="test123", email="org1_manager@test.com" ) self._create_org_user(organization=org1, user=org1_manager, is_admin=True) - administrator = Group.objects.get(name='Administrator') + administrator = Group.objects.get(name="Administrator") org1_manager.groups.add(administrator) self.client.force_login(org1_manager) - with self.subTest('test user list'): - path = reverse('users:user_list') + with self.subTest("test user list"): + path = reverse("users:user_list") with self.assertNumQueries(9): r = self.client.get(path) self.assertEqual(r.status_code, 200) - self.assertNotIn('is_superuser', str(r.content)) + self.assertNotIn("is_superuser", str(r.content)) - with self.subTest('test create org user'): + with self.subTest("test create org user"): self.assertEqual(User.objects.count(), 3) data = { - 'username': 'user2org1', - 'password': 'password', - 'email': 'user2org1@test.com', - 'organization_users': {'is_admin': False, 'organization': org1.pk}, + "username": "user2org1", + "password": "password", + "email": "user2org1@test.com", + "organization_users": {"is_admin": False, "organization": org1.pk}, } - r = self.client.post(path, data, content_type='application/json') + r = self.client.post(path, data, content_type="application/json") self.assertEqual(r.status_code, 201) - self.assertNotIn('is_superuser', r.data) + self.assertNotIn("is_superuser", r.data) - with self.subTest('test user detail'): - path = reverse('users:user_detail', args=(org1_manager.pk,)) + with self.subTest("test user detail"): + path = reverse("users:user_detail", args=(org1_manager.pk,)) with self.assertNumQueries(8): r = self.client.get(path) self.assertEqual(r.status_code, 200) - with self.subTest('test update user data'): - path = reverse('users:user_detail', args=(org1_manager.pk,)) - data = {'username': 'changetestuser'} + with self.subTest("test update user data"): + path = reverse("users:user_detail", args=(org1_manager.pk,)) + data = {"username": "changetestuser"} with self.assertNumQueries(13): - r = self.client.patch(path, data, content_type='application/json') + r = self.client.patch(path, data, content_type="application/json") self.assertEqual(r.status_code, 200) - self.assertEqual(r.data['username'], 'changetestuser') + self.assertEqual(r.data["username"], "changetestuser") def test_organization_slug_post_custom_validation_api(self): - path = reverse('users:organization_list') - data = {'name': 'test-org', 'slug': 'test-org'} + path = reverse("users:organization_list") + data = {"name": "test-org", "slug": "test-org"} with self.assertNumQueries(6): - r = self.client.post(path, data, content_type='application/json') + r = self.client.post(path, data, content_type="application/json") self.assertEqual(r.status_code, 201) self.assertEqual(Organization.objects.count(), 2) # try to create a new organization with the same slug - r = self.client.post(path, data, content_type='application/json') + r = self.client.post(path, data, content_type="application/json") self.assertEqual(r.status_code, 400) self.assertEqual(Organization.objects.count(), 2) - self.assertListEqual(list(r.data.keys()), ['slug', 'organization']) + self.assertListEqual(list(r.data.keys()), ["slug", "organization"]) diff --git a/openwisp_users/tests/test_api/test_authentication.py b/openwisp_users/tests/test_api/test_authentication.py index e44684922..8a6f8f8e8 100644 --- a/openwisp_users/tests/test_api/test_authentication.py +++ b/openwisp_users/tests/test_api/test_authentication.py @@ -25,24 +25,24 @@ def setUp(self): self.operator = self._create_operator() def test_bearer_authentication(self): - @api_view(['GET']) + @api_view(["GET"]) @permission_classes([IsAuthenticated]) @authentication_classes([BearerAuthentication]) def my_view(request): return Response({}) - request = self.factory.get('/') + request = self.factory.get("/") response = my_view(request) self.assertEqual(response.status_code, 401) token = self._obtain_auth_token() - request = self.factory.get('/', HTTP_AUTHORIZATION=f'Bearer {token}') + request = self.factory.get("/", HTTP_AUTHORIZATION=f"Bearer {token}") response = my_view(request) self.assertEqual(response.status_code, 200) - @modify_settings(AUTHENTICATION_BACKENDS={'append': 'sesame.backends.ModelBackend'}) + @modify_settings(AUTHENTICATION_BACKENDS={"append": "sesame.backends.ModelBackend"}) def test_sesame_authentication(self): - @api_view(['GET']) + @api_view(["GET"]) @permission_classes([IsAuthenticated]) @authentication_classes([SesameAuthentication]) def my_view(request): @@ -51,43 +51,43 @@ def my_view(request): user = User.objects.first() token = get_one_time_auth_token_for_user(user) - with self.subTest('Test without header'): - request = self.factory.get('/') + with self.subTest("Test without header"): + request = self.factory.get("/") response = my_view(request) self.assertEqual(response.status_code, 403) - with self.subTest('Test token missing'): + with self.subTest("Test token missing"): request = self.factory.get( - '/', HTTP_AUTHORIZATION=f'{sesame_settings.TOKEN_NAME}' + "/", HTTP_AUTHORIZATION=f"{sesame_settings.TOKEN_NAME}" ) response = my_view(request) self.assertEqual(response.status_code, 403) - with self.subTest('Test extra key'): + with self.subTest("Test extra key"): request = self.factory.get( - '/', HTTP_AUTHORIZATION=f'{sesame_settings.TOKEN_NAME} {token} extrakey' + "/", HTTP_AUTHORIZATION=f"{sesame_settings.TOKEN_NAME} {token} extrakey" ) response = my_view(request) self.assertEqual(response.status_code, 403) - with self.subTest('Test UnicodeError'): + with self.subTest("Test UnicodeError"): request = self.factory.get( - '/', HTTP_AUTHORIZATION=f'{sesame_settings.TOKEN_NAME} "¸"' + "/", HTTP_AUTHORIZATION=f'{sesame_settings.TOKEN_NAME} "¸"' ) response = my_view(request) self.assertEqual(response.status_code, 403) - with self.subTest('Test with invalid token'): + with self.subTest("Test with invalid token"): request = self.factory.get( - '/', HTTP_AUTHORIZATION=f'{sesame_settings.TOKEN_NAME} token' + "/", HTTP_AUTHORIZATION=f"{sesame_settings.TOKEN_NAME} token" ) response = my_view(request) self.assertEqual(response.status_code, 403) - self.assertEqual(str(response.data['detail']), 'Invalid or expired token.') + self.assertEqual(str(response.data["detail"]), "Invalid or expired token.") - with self.subTest('Test ideal flow'): + with self.subTest("Test ideal flow"): request = self.factory.get( - '/', HTTP_AUTHORIZATION=f'{sesame_settings.TOKEN_NAME} {token}' + "/", HTTP_AUTHORIZATION=f"{sesame_settings.TOKEN_NAME} {token}" ) response = my_view(request) self.assertEqual(response.status_code, 200) diff --git a/openwisp_users/tests/test_api/test_throttling.py b/openwisp_users/tests/test_api/test_throttling.py index fb73689f9..3c9d03d61 100644 --- a/openwisp_users/tests/test_api/test_throttling.py +++ b/openwisp_users/tests/test_api/test_throttling.py @@ -12,8 +12,8 @@ def setUp(self): self._create_operator() def test_auth_rate_throttle(self): - AuthRateThrottle.rate = '1/day' - url = reverse('users:user_auth_token') + AuthRateThrottle.rate = "1/day" + url = reverse("users:user_auth_token") data = {"username": "operator", "password": "tester"} r = self.client.post(url, data) self.assertEqual(r.status_code, 200) diff --git a/openwisp_users/tests/test_api/test_views.py b/openwisp_users/tests/test_api/test_views.py index 2d17c35d7..067aefb79 100644 --- a/openwisp_users/tests/test_api/test_views.py +++ b/openwisp_users/tests/test_api/test_views.py @@ -10,17 +10,17 @@ def setUp(self): cache.clear() def test_obtain_auth_token(self): - self._create_user(username='tester', password='tester') - params = {'username': 'tester', 'password': 'tester'} - url = reverse('users:user_auth_token') + self._create_user(username="tester", password="tester") + params = {"username": "tester", "password": "tester"} + url = reverse("users:user_auth_token") r = self.client.post(url, params) - self.assertIn('token', r.data) + self.assertIn("token", r.data) def test_protected_api_mixin_view(self): - auth_error = 'Authentication credentials were not provided.' - user = self._create_user(username='tester', password='tester') - path = reverse('users:user_detail', args=(user.pk,)) + auth_error = "Authentication credentials were not provided." + user = self._create_user(username="tester", password="tester") + path = reverse("users:user_detail", args=(user.pk,)) response = self.client.get(path) - self.assertEqual(response.headers['WWW-Authenticate'], 'Bearer') - self.assertEqual(response.data['detail'], auth_error) + self.assertEqual(response.headers["WWW-Authenticate"], "Bearer") + self.assertEqual(response.data["detail"], auth_error) self.assertEqual(response.status_code, 401) diff --git a/openwisp_users/tests/test_backends.py b/openwisp_users/tests/test_backends.py index 02aba9e73..b9b8e0f73 100644 --- a/openwisp_users/tests/test_backends.py +++ b/openwisp_users/tests/test_backends.py @@ -15,141 +15,141 @@ class TestBackends(TestOrganizationMixin, TestCase): def _test_user_auth_backend_helper(self, username, password, pk): self.client.login(username=username, password=password) - self.assertIn('_auth_user_id', self.client.session) + self.assertIn("_auth_user_id", self.client.session) self.assertEqual( - UUID(self.client.session['_auth_user_id'], version=4), + UUID(self.client.session["_auth_user_id"], version=4), pk, ) self.client.logout() - self.assertNotIn('_auth_user_id', self.client.session) + self.assertNotIn("_auth_user_id", self.client.session) @override_settings( - AUTHENTICATION_BACKENDS=('openwisp_users.backends.UsersAuthenticationBackend',) + AUTHENTICATION_BACKENDS=("openwisp_users.backends.UsersAuthenticationBackend",) ) def test_user_auth_backend(self): user = self._create_user( - username='tester', - email='tester@gmail.com', - phone_number='+237675579231', - password='tester', + username="tester", + email="tester@gmail.com", + phone_number="+237675579231", + password="tester", ) - with self.subTest('Test login with username'): - self._test_user_auth_backend_helper('tester', 'tester', user.pk) + with self.subTest("Test login with username"): + self._test_user_auth_backend_helper("tester", "tester", user.pk) - with self.subTest('Test login with email'): - self._test_user_auth_backend_helper('tester@gmail.com', 'tester', user.pk) + with self.subTest("Test login with email"): + self._test_user_auth_backend_helper("tester@gmail.com", "tester", user.pk) - with self.subTest('Test login with phone_number'): - self._test_user_auth_backend_helper('+237675579231', 'tester', user.pk) + with self.subTest("Test login with phone_number"): + self._test_user_auth_backend_helper("+237675579231", "tester", user.pk) @override_settings( - AUTHENTICATION_BACKENDS=('openwisp_users.backends.UsersAuthenticationBackend',) + AUTHENTICATION_BACKENDS=("openwisp_users.backends.UsersAuthenticationBackend",) ) def test_user_with_email_as_username_auth_backend(self): user = self._create_user( - username='tester', - email='tester@gmail.com', - phone_number='+237675579231', - password='tester', + username="tester", + email="tester@gmail.com", + phone_number="+237675579231", + password="tester", ) self._create_user( - username='tester@gmail.com', - email='tester1@gmail.com', - phone_number='+237675579232', - password='tester1', + username="tester@gmail.com", + email="tester1@gmail.com", + phone_number="+237675579232", + password="tester1", ) - self._test_user_auth_backend_helper(user.email, 'tester', user.pk) + self._test_user_auth_backend_helper(user.email, "tester", user.pk) @override_settings( - AUTHENTICATION_BACKENDS=('openwisp_users.backends.UsersAuthenticationBackend',) + AUTHENTICATION_BACKENDS=("openwisp_users.backends.UsersAuthenticationBackend",) ) def test_user_with_phone_number_as_username_auth_backend(self): user = self._create_user( - username='tester', - email='tester@gmail.com', - phone_number='+237675579231', - password='tester', + username="tester", + email="tester@gmail.com", + phone_number="+237675579231", + password="tester", ) self._create_user( - username='+237675579231', - email='tester1@gmail.com', - phone_number='+237675579232', - password='tester1', + username="+237675579231", + email="tester1@gmail.com", + phone_number="+237675579232", + password="tester1", ) - self._test_user_auth_backend_helper(user.phone_number, 'tester', user.pk) + self._test_user_auth_backend_helper(user.phone_number, "tester", user.pk) def test_auth_backend_get_users(self): user = self._create_user( - username='tester', - email='tester@gmail.com', - phone_number='+237675579231', - password='tester', + username="tester", + email="tester@gmail.com", + phone_number="+237675579231", + password="tester", ) user1 = self._create_user( - username='tester1', - email='tester1@gmail.com', - phone_number='+237675579232', - password='tester1', + username="tester1", + email="tester1@gmail.com", + phone_number="+237675579232", + password="tester1", ) - with self.subTest('get user with invalid identifier'): - self.assertEqual(len(auth_backend.get_users('invalid')), 0) + with self.subTest("get user with invalid identifier"): + self.assertEqual(len(auth_backend.get_users("invalid")), 0) - with self.subTest('get user with email'): + with self.subTest("get user with email"): user1.username = user.email user1.save() self.assertEqual(auth_backend.get_users(user.email)[0], user) - with self.subTest('get user with phone_number'): + with self.subTest("get user with phone_number"): user1.username = user.phone_number user1.save() self.assertEqual(auth_backend.get_users(user.phone_number)[0], user) - with self.subTest('get user with username'): + with self.subTest("get user with username"): self.assertEqual(auth_backend.get_users(user.username)[0], user) @override_settings( - AUTHENTICATION_BACKENDS=('openwisp_users.backends.UsersAuthenticationBackend',), + AUTHENTICATION_BACKENDS=("openwisp_users.backends.UsersAuthenticationBackend",), ) def test_accept_flexible_phone_number_format(self): user1 = self._create_user( - username='tester1', - email='tester1@test.com', - phone_number='+393665243702', - password='tester1', + username="tester1", + email="tester1@test.com", + phone_number="+393665243702", + password="tester1", ) variants = ( - '+39 3665243702', - '+39 366.52.43.702', - '+39 366.52 43-702', + "+39 3665243702", + "+39 366.52.43.702", + "+39 366.52 43-702", ) for variant in variants: self.assertEqual(auth_backend.get_users(variant).count(), 1) self.assertEqual(auth_backend.get_users(variant).first(), user1) @override_settings( - AUTHENTICATION_BACKENDS=('openwisp_users.backends.UsersAuthenticationBackend',), + AUTHENTICATION_BACKENDS=("openwisp_users.backends.UsersAuthenticationBackend",), ) - @mock.patch.object(users_settings, 'AUTH_BACKEND_AUTO_PREFIXES', ('+39',)) + @mock.patch.object(users_settings, "AUTH_BACKEND_AUTO_PREFIXES", ("+39",)) def test_partial_phone_number(self): user1 = self._create_user( - username='tester1', - email='tester1@test.com', - phone_number='+393665243702', - password='tester1', + username="tester1", + email="tester1@test.com", + phone_number="+393665243702", + password="tester1", ) - self.assertEqual(auth_backend.get_users('3665243702').count(), 1) - self.assertEqual(auth_backend.get_users('3665243702').first(), user1) + self.assertEqual(auth_backend.get_users("3665243702").count(), 1) + self.assertEqual(auth_backend.get_users("3665243702").first(), user1) - with self.subTest('test with leading zero'): - self.assertEqual(auth_backend.get_users('03665243702').count(), 1) - self.assertEqual(auth_backend.get_users('03665243702').first(), user1) + with self.subTest("test with leading zero"): + self.assertEqual(auth_backend.get_users("03665243702").count(), 1) + self.assertEqual(auth_backend.get_users("03665243702").first(), user1) - with self.subTest('test different prefix which is not enabled'): + with self.subTest("test different prefix which is not enabled"): self._create_user( - username='tester2', - email='tester2@test.com', - phone_number='+51911524370', - password='tester2', + username="tester2", + email="tester2@test.com", + phone_number="+51911524370", + password="tester2", ) - self.assertEqual(auth_backend.get_users('911524370').count(), 0) + self.assertEqual(auth_backend.get_users("911524370").count(), 0) diff --git a/openwisp_users/tests/test_commands.py b/openwisp_users/tests/test_commands.py index 69838d675..69b4131f0 100644 --- a/openwisp_users/tests/test_commands.py +++ b/openwisp_users/tests/test_commands.py @@ -16,15 +16,15 @@ class TestManagementCommands(TestOrganizationMixin, TestCase): def setUp(self): super().setUp() - self.temp_file = NamedTemporaryFile(mode='wt', delete=False) + self.temp_file = NamedTemporaryFile(mode="wt", delete=False) def tearDown(self): super().tearDown() self.temp_file.close() def test_export_users(self): - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") user = self._create_user() operator = self._create_operator() admin = self._create_admin() @@ -33,74 +33,74 @@ def test_export_users(self): self._create_org_user(organization=org2, user=operator, is_admin=False) stdout = StringIO() with self.assertNumQueries(2): - call_command('export_users', filename=self.temp_file.name, stdout=stdout) + call_command("export_users", filename=self.temp_file.name, stdout=stdout) self.assertIn( - f'User data exported successfully to {self.temp_file.name}', + f"User data exported successfully to {self.temp_file.name}", stdout.getvalue(), ) # Read the content of the temporary file - with open(self.temp_file.name, 'r') as temp_file: + with open(self.temp_file.name, "r") as temp_file: csv_reader = csv.reader(temp_file) csv_data = list(csv_reader) # 3 user and 1 header self.assertEqual(len(csv_data), 4) self.assertEqual( - csv_data[0], app_settings.EXPORT_USERS_COMMAND_CONFIG['fields'] + csv_data[0], app_settings.EXPORT_USERS_COMMAND_CONFIG["fields"] ) # Ensuring ordering self.assertEqual(csv_data[1][0], str(user.id)) self.assertEqual(csv_data[2][0], str(operator.id)) self.assertEqual(csv_data[3][0], str(admin.id)) # Check organizations formatting - self.assertEqual(csv_data[1][-1], f'({org1.id},True)\n({org2.id},False)') - self.assertEqual(csv_data[2][-1], f'({org2.id},False)') - self.assertEqual(csv_data[3][-1], '') + self.assertEqual(csv_data[1][-1], f"({org1.id},True)\n({org2.id},False)") + self.assertEqual(csv_data[2][-1], f"({org2.id},False)") + self.assertEqual(csv_data[3][-1], "") @capture_stdout() def test_exclude_fields(self): self._create_user() call_command( - 'export_users', + "export_users", filename=self.temp_file.name, - exclude_fields=','.join( - app_settings.EXPORT_USERS_COMMAND_CONFIG['fields'][1:] + exclude_fields=",".join( + app_settings.EXPORT_USERS_COMMAND_CONFIG["fields"][1:] ), ) - with open(self.temp_file.name, 'r') as temp_file: + with open(self.temp_file.name, "r") as temp_file: csv_reader = csv.reader(temp_file) csv_data = list(csv_reader) # 1 user and 1 header self.assertEqual(len(csv_data), 2) - self.assertEqual(csv_data[0], ['id']) + self.assertEqual(csv_data[0], ["id"]) @patch.object( app_settings, - 'EXPORT_USERS_COMMAND_CONFIG', - {'fields': ['id', 'auth_token.key']}, + "EXPORT_USERS_COMMAND_CONFIG", + {"fields": ["id", "auth_token.key"]}, ) def test_related_fields(self): user = self._create_user() token = Token.objects.create(user=user) stdout = StringIO() with self.assertNumQueries(2): - call_command('export_users', filename=self.temp_file.name, stdout=stdout) + call_command("export_users", filename=self.temp_file.name, stdout=stdout) self.assertIn( - f'User data exported successfully to {self.temp_file.name}', + f"User data exported successfully to {self.temp_file.name}", stdout.getvalue(), ) # Read the content of the temporary file - with open(self.temp_file.name, 'r') as temp_file: + with open(self.temp_file.name, "r") as temp_file: csv_reader = csv.reader(temp_file) csv_data = list(csv_reader) # 3 user and 1 header self.assertEqual(len(csv_data), 2) self.assertEqual( - csv_data[0], app_settings.EXPORT_USERS_COMMAND_CONFIG['fields'] + csv_data[0], app_settings.EXPORT_USERS_COMMAND_CONFIG["fields"] ) self.assertEqual(csv_data[1][0], str(user.id)) self.assertEqual(csv_data[1][1], str(token.key)) diff --git a/openwisp_users/tests/test_middlewares.py b/openwisp_users/tests/test_middlewares.py index 14ec48532..79bfe59bd 100644 --- a/openwisp_users/tests/test_middlewares.py +++ b/openwisp_users/tests/test_middlewares.py @@ -14,36 +14,36 @@ class TestPasswordExpirationMiddleware(TestOrganizationMixin, TestCase): @modify_settings( MIDDLEWARE={ - 'remove': ['openwisp_users.middleware.PasswordExpirationMiddleware'] + "remove": ["openwisp_users.middleware.PasswordExpirationMiddleware"] } ) - @patch.object(app_settings, 'STAFF_USER_PASSWORD_EXPIRATION', 10) + @patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 10) def test_queries_middleware_absent(self): admin = self._create_admin() with self.assertNumQueries(2): response = self.client.post( - reverse('admin:login'), - data={'username': admin.username, 'password': 'tester'}, + reverse("admin:login"), + data={"username": admin.username, "password": "tester"}, ) self.assertEqual(response.status_code, 302) - self.assertEqual(response.url, '/admin/') + self.assertEqual(response.url, "/admin/") with self.assertNumQueries(1): self.client.force_login(admin) @modify_settings( MIDDLEWARE={ - 'append': ['openwisp_users.middleware.PasswordExpirationMiddleware'] + "append": ["openwisp_users.middleware.PasswordExpirationMiddleware"] } ) - @patch.object(app_settings, 'STAFF_USER_PASSWORD_EXPIRATION', 10) + @patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 10) def test_queries_middleware_present(self): admin = self._create_admin(password_updated=now().date() - timedelta(days=180)) with self.assertNumQueries(2): response = self.client.post( - reverse('admin:login'), - data={'username': admin.username, 'password': 'tester'}, + reverse("admin:login"), + data={"username": admin.username, "password": "tester"}, ) self.assertEqual(response.status_code, 302) - self.assertEqual(response.url, '/accounts/password/change/?next=/admin/') + self.assertEqual(response.url, "/accounts/password/change/?next=/admin/") with self.assertNumQueries(1): self.client.force_login(admin) diff --git a/openwisp_users/tests/test_models.py b/openwisp_users/tests/test_models.py index 89dff70ad..df463e91c 100644 --- a/openwisp_users/tests/test_models.py +++ b/openwisp_users/tests/test_models.py @@ -14,9 +14,9 @@ from ..tasks import password_expiration_email from .utils import TestOrganizationMixin -Organization = load_model('openwisp_users', 'Organization') -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') -OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner') +Organization = load_model("openwisp_users", "Organization") +OrganizationUser = load_model("openwisp_users", "OrganizationUser") +OrganizationOwner = load_model("openwisp_users", "OrganizationOwner") EmailConfirmation = get_emailconfirmation_model() User = get_user_model() @@ -26,45 +26,45 @@ class TestUsers(TestOrganizationMixin, TestCase): def test_create_superuser_email(self): user = User.objects.create_superuser( - username='tester', password='tester', email='test@superuser.com' + username="tester", password="tester", email="test@superuser.com" ) self.assertEqual(user.emailaddress_set.count(), 1) - self.assertEqual(user.emailaddress_set.first().email, 'test@superuser.com') + self.assertEqual(user.emailaddress_set.first().email, "test@superuser.com") def test_create_superuser_email_empty(self): user = User.objects.create_superuser( - username='tester', password='tester', email='' + username="tester", password="tester", email="" ) self.assertEqual(user.emailaddress_set.count(), 0) def test_unique_email_validation(self): - self._create_user(username='user1', email='same@gmail.com') - options = {'username': 'user2', 'email': 'same@gmail.com', 'password': 'pass1'} + self._create_user(username="user1", email="same@gmail.com") + options = {"username": "user2", "email": "same@gmail.com", "password": "pass1"} u = self.user_model(**options) with self.assertRaises(ValidationError): u.full_clean() def test_create_user_without_email(self): - options = {'username': 'testuser', 'password': 'test1'} + options = {"username": "testuser", "password": "test1"} u = self.user_model(**options) u.full_clean() u.save() self.assertIsNone(u.email) def test_organizations_dict(self): - user = self._create_user(username='organizations_pk') + user = self._create_user(username="organizations_pk") self.assertEqual(user.organizations_dict, {}) - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') - self._create_org(name='org3') + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") + self._create_org(name="org3") ou1 = OrganizationUser.objects.create( user=user, organization=org1, is_admin=True ) ou2 = OrganizationUser.objects.create(user=user, organization=org2) expected = { - str(org1.pk): {'is_admin': ou1.is_admin, 'is_owner': True}, - str(org2.pk): {'is_admin': ou2.is_admin, 'is_owner': False}, + str(org1.pk): {"is_admin": ou1.is_admin, "is_owner": True}, + str(org2.pk): {"is_admin": ou2.is_admin, "is_owner": False}, } self.assertEqual(user.organizations_dict, expected) self.assertEqual(len(user.organizations_dict), 2) @@ -75,8 +75,8 @@ def test_organizations_dict(self): self.assertEqual(user.organizations_dict, expected) def test_organizations_dict_cache(self): - user = self._create_user(username='organizations_pk') - org1 = self._create_org(name='org1') + user = self._create_user(username="organizations_pk") + org1 = self._create_org(name="org1") with self.assertNumQueries(1): list(user.organizations_dict) @@ -91,27 +91,27 @@ def test_organizations_dict_cache(self): list(user.organizations_dict) def test_is_member(self): - user = self._create_user(username='organizations_pk') - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') + user = self._create_user(username="organizations_pk") + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") - with self.subTest('org instance'): + with self.subTest("org instance"): self.assertFalse(user.is_member(org1)) self.assertFalse(user.is_member(org2)) OrganizationUser.objects.create(user=user, organization=org1) self.assertTrue(user.is_member(org1)) self.assertFalse(user.is_member(org2)) - with self.subTest('org pk'): + with self.subTest("org pk"): self.assertTrue(user.is_member(org1.pk)) self.assertFalse(user.is_member(str(org2.pk))) def test_is_manager(self): - user = self._create_user(username='organizations_pk') - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') + user = self._create_user(username="organizations_pk") + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") - with self.subTest('org instance'): + with self.subTest("org instance"): self.assertFalse(user.is_manager(org1)) self.assertFalse(user.is_manager(org2)) ou = OrganizationUser.objects.create(user=user, organization=org1) @@ -122,35 +122,35 @@ def test_is_manager(self): self.assertTrue(user.is_manager(org1)) self.assertFalse(user.is_manager(org2)) - with self.subTest('org pk'): + with self.subTest("org pk"): self.assertTrue(user.is_manager(org1.pk)) self.assertFalse(user.is_manager(str(org2.pk))) def test_is_owner(self): - user = self._create_user(username='organizations_pk') - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') + user = self._create_user(username="organizations_pk") + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") - with self.subTest('org instance'): + with self.subTest("org instance"): self.assertFalse(user.is_owner(org1)) self.assertFalse(user.is_owner(org2)) OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) self.assertTrue(user.is_owner(org1)) self.assertFalse(user.is_owner(org2)) - with self.subTest('org pk'): + with self.subTest("org pk"): self.assertTrue(user.is_owner(org1.pk)) self.assertFalse(user.is_owner(str(org2.pk))) def test_invalidate_cache_org_owner_user_changed(self): org = self._get_org() user1 = self._create_user( - username='user1', - email='user1@test.org', + username="user1", + email="user1@test.org", ) user2 = self._create_user( - username='user2', - email='user2@test.org', + username="user2", + email="user2@test.org", ) org_user1 = self._create_org_user(user=user1, organization=org, is_admin=True) org_user2 = self._create_org_user(user=user2, organization=org, is_admin=True) @@ -170,12 +170,12 @@ def test_invalidate_cache_org_owner_user_changed(self): def test_invalidate_cache_org_user_user_changed(self): org = self._get_org() user1 = self._create_user( - username='user1', - email='user1@test.org', + username="user1", + email="user1@test.org", ) user2 = self._create_user( - username='user2', - email='user2@test.org', + username="user2", + email="user2@test.org", ) org_user = self._create_org_user(user=user1, organization=org, is_admin=True) self.assertEqual(user1.is_member(org), True) @@ -187,8 +187,8 @@ def test_invalidate_cache_org_user_user_changed(self): self.assertEqual(user2.is_member(org), True) def test_invalidate_cache_org_status_changed(self): - org = self._create_org(name='testorg1') - user1 = self._create_user(username='testuser1', email='user1@test.com') + org = self._create_org(name="testorg1") + user1 = self._create_user(username="testuser1", email="user1@test.com") self._create_org_user(user=user1, organization=org) self.assertEqual(user1.is_member(org), True) org.is_active = False @@ -197,35 +197,35 @@ def test_invalidate_cache_org_status_changed(self): self.assertEqual(user1.is_member(org), False) def test_organizations_managed(self): - user = self._create_user(username='organizations_pk') + user = self._create_user(username="organizations_pk") self.assertEqual(user.organizations_managed, []) - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') - org3 = self._create_org(name='org3') + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") + org3 = self._create_org(name="org3") OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) OrganizationUser.objects.create(user=user, organization=org2, is_admin=True) OrganizationUser.objects.create(user=user, organization=org3, is_admin=False) self.assertEqual(user.organizations_managed, [str(org1.pk), str(org2.pk)]) def test_organizations_owned(self): - user = self._create_user(username='organizations_pk') + user = self._create_user(username="organizations_pk") self.assertEqual(user.organizations_managed, []) - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') - org3 = self._create_org(name='org3') + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") + org3 = self._create_org(name="org3") OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) OrganizationUser.objects.create(user=user, organization=org2, is_admin=True) OrganizationUser.objects.create(user=user, organization=org3, is_admin=False) self.assertEqual(user.organizations_owned, [str(org1.pk), str(org2.pk)]) def test_organization_repr(self): - org = self._create_org(name='org1', is_active=False) - self.assertIn('disabled', str(org)) + org = self._create_org(name="org1", is_active=False) + self.assertIn("disabled", str(org)) def test_organization_owner_bad_organization(self): - user = self._create_user(username='user1', email='abc@example.com') - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') + user = self._create_user(username="user1", email="abc@example.com") + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") org_user = self._create_org_user(organization=org1, user=user) org_owner = self._create_org_owner() org_owner.organization = org2 @@ -234,12 +234,12 @@ def test_organization_owner_bad_organization(self): org_owner.full_clean() def test_create_users_without_email(self): - options = {'username': 'testuser', 'password': 'test1'} + options = {"username": "testuser", "password": "test1"} u = self.user_model(**options) u.full_clean() u.save() self.assertIsNone(u.email) - options['username'] = 'testuser2' + options["username"] = "testuser2" u = self.user_model(**options) u.full_clean() u.save() @@ -248,16 +248,16 @@ def test_create_users_without_email(self): def test_add_users_with_empty_phone_numbers(self): user1 = self.user_model( - username='user1', - email='email1@email.com', - password='user1', - phone_number='', + username="user1", + email="email1@email.com", + password="user1", + phone_number="", ) user2 = self.user_model( - username='user2', - email='email2@email.com', - password='user2', - phone_number='', + username="user2", + email="email2@email.com", + password="user2", + phone_number="", ) user1.full_clean() user2.full_clean() @@ -270,47 +270,47 @@ def test_add_users_with_empty_phone_numbers(self): def test_user_get_pk(self): org = Organization.objects.first() pk = str(org.pk) - with self.subTest('standard tests'): + with self.subTest("standard tests"): self.assertEqual(User._get_pk(org), pk) self.assertEqual(User._get_pk(org.pk), pk) self.assertEqual(User._get_pk(pk), pk) - with self.subTest('None case'): + with self.subTest("None case"): self.assertEqual(User._get_pk(None), None) - with self.subTest('ValueError if another type passed'): + with self.subTest("ValueError if another type passed"): with self.assertRaises(ValueError) as context_manager: User._get_pk([]) self.assertEqual( str(context_manager.exception), - 'expected UUID, str or Organization instance', + "expected UUID, str or Organization instance", ) def test_orguser_is_admin_change(self): - org = self._create_org(name='test-org') - user1 = self._create_user(username='user1', email='user1@email.com') - user2 = self._create_user(username='user2', email='user2@email.com') + org = self._create_org(name="test-org") + user1 = self._create_user(username="user1", email="user1@email.com") + user2 = self._create_user(username="user2", email="user2@email.com") org_user1 = self._create_org_user(user=user1, organization=org, is_admin=True) org_user2 = self._create_org_user(user=user2, organization=org, is_admin=True) - with self.subTest('change is_admin when org_user belongs to org_owner'): + with self.subTest("change is_admin when org_user belongs to org_owner"): msg = ( - f'{user1.username} is the owner of the organization: ' - f'{org}, and cannot be downgraded' + f"{user1.username} is the owner of the organization: " + f"{org}, and cannot be downgraded" ) with self.assertRaisesMessage(ValidationError, msg): with self.assertNumQueries(1): org_user1.is_admin = False org_user1.full_clean() - with self.subTest('change is_admin when org_user doesnot belong to orgowner'): + with self.subTest("change is_admin when org_user doesnot belong to orgowner"): org_user2.is_admin = False org_user2.full_clean() org_user2.save() self.assertEqual(org_user2.is_admin, False) @override_settings( - ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL='email_confirmation_success', + ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL="email_confirmation_success", ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL=( - 'email_confirmation_success' + "email_confirmation_success" ), ) def test_email_verification_success(self): @@ -319,18 +319,18 @@ def test_email_verification_success(self): email_address = user.emailaddress_set.first() email_confirmation = EmailConfirmation.create(email_address) email_confirmation.send() - url = reverse('account_confirm_email', args=[email_confirmation.key]) + url = reverse("account_confirm_email", args=[email_confirmation.key]) response = self.client.post(url, follow=True) - self.assertContains(response, 'Your email has been verified successfully.') - self.assertContains(response, 'This web page can be closed.') + self.assertContains(response, "Your email has been verified successfully.") + self.assertContains(response, "This web page can be closed.") - @override_settings(ACCOUNT_LOGOUT_REDIRECT_URL='logout_success') + @override_settings(ACCOUNT_LOGOUT_REDIRECT_URL="logout_success") def test_logout_success(self): user = self._create_user() self.client.force_login(user) - response = self.client.post(reverse('account_logout'), follow=True) - self.assertContains(response, 'Logout successful.') - self.assertContains(response, 'This web page can be closed.') + response = self.client.post(reverse("account_logout"), follow=True) + self.assertContains(response, "Logout successful.") + self.assertContains(response, "This web page can be closed.") def test_organization_user_string_representation(self): org = self._get_org() @@ -340,18 +340,18 @@ def test_organization_user_string_representation(self): user=user, ) - with self.subTest('Test user first and last names are mot empty'): + with self.subTest("Test user first and last names are mot empty"): self.assertEqual( - str(org_user), f'{user.first_name} {user.last_name} ({org.name})' + str(org_user), f"{user.first_name} {user.last_name} ({org.name})" ) - user.first_name = '' - user.last_name = '' + user.first_name = "" + user.last_name = "" user.save() org_user.refresh_from_db() - with self.subTest('Test user first and last names are empty'): - self.assertEqual(str(org_user), f'{user.username} ({org.name})') + with self.subTest("Test user first and last names are empty"): + self.assertEqual(str(org_user), f"{user.username} ({org.name})") def test_add_user(self): org = self._get_org() @@ -370,40 +370,40 @@ def test_has_password_expired(self): staff_user.refresh_from_db() end_user.refresh_from_db() - with self.subTest('Test password expiration feature disabled'): + with self.subTest("Test password expiration feature disabled"): with patch.object( - app_settings, 'USER_PASSWORD_EXPIRATION', 0 - ), patch.object(app_settings, 'STAFF_USER_PASSWORD_EXPIRATION', 0): + app_settings, "USER_PASSWORD_EXPIRATION", 0 + ), patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 0): self.assertEqual(staff_user.has_password_expired(), False) self.assertEqual(end_user.has_password_expired(), False) - with self.subTest('Test password is not expired'): + with self.subTest("Test password is not expired"): with patch.object( - app_settings, 'USER_PASSWORD_EXPIRATION', 10 - ), patch.object(app_settings, 'STAFF_USER_PASSWORD_EXPIRATION', 10): + app_settings, "USER_PASSWORD_EXPIRATION", 10 + ), patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 10): self.assertEqual(staff_user.has_password_expired(), False) self.assertEqual(end_user.has_password_expired(), False) - with self.subTest('Test password is expired'): + with self.subTest("Test password is expired"): User.objects.update(password_updated=now().date() - timedelta(days=180)) staff_user.refresh_from_db() end_user.refresh_from_db() with patch.object( - app_settings, 'USER_PASSWORD_EXPIRATION', 10 - ), patch.object(app_settings, 'STAFF_USER_PASSWORD_EXPIRATION', 10): + app_settings, "USER_PASSWORD_EXPIRATION", 10 + ), patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 10): self.assertEqual(staff_user.has_password_expired(), True) self.assertEqual(end_user.has_password_expired(), True) - with self.subTest('Test password_updated is None'): + with self.subTest("Test password_updated is None"): User.objects.update(password_updated=None) end_user.refresh_from_db() with patch.object( - app_settings, 'USER_PASSWORD_EXPIRATION', 10 - ), patch.object(app_settings, 'STAFF_USER_PASSWORD_EXPIRATION', 10): + app_settings, "USER_PASSWORD_EXPIRATION", 10 + ), patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 10): self.assertEqual(end_user.has_password_expired(), False) - @patch.object(app_settings, 'USER_PASSWORD_EXPIRATION', 30) - @patch.object(app_settings, 'STAFF_USER_PASSWORD_EXPIRATION', 90) + @patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 30) + @patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 90) def test_password_expiration_mail(self): user_expiry_date = now().date() - timedelta( days=(app_settings.USER_PASSWORD_EXPIRATION - 7) @@ -415,7 +415,7 @@ def test_password_expiration_mail(self): end_user = self._create_user() with self.subTest( - 'Test end-user and staff user has different expiration dates' + "Test end-user and staff user has different expiration dates" ): User.objects.filter(is_staff=False).update( password_updated=user_expiry_date @@ -430,10 +430,10 @@ def test_password_expiration_mail(self): staff_user.delete() - with self.subTest('Test email is sent to users with verified email'): + with self.subTest("Test email is sent to users with verified email"): unverified_email_user = self._create_user( - username='unverified_email', - email='unverified_email@example.com', + username="unverified_email", + email="unverified_email@example.com", ) # Re-using object to make tests run quickly verified_email_user = end_user @@ -448,34 +448,34 @@ def test_password_expiration_mail(self): self.assertEqual(len(mail.outbox), 1) email = mail.outbox.pop() self.assertEqual(email.to, [verified_email_user.email]) - self.assertEqual(email.subject, 'Action Required: Password Expiry Notice') + self.assertEqual(email.subject, "Action Required: Password Expiry Notice") self.assertEqual( email.body, - 'We inform you that the password for your account tester will expire' - f' in 7 days, precisely on {expected_date}.\n\n' - 'Kindly proceed with updating your password by clicking on the' - ' button below.', + "We inform you that the password for your account tester will expire" + f" in 7 days, precisely on {expected_date}.\n\n" + "Kindly proceed with updating your password by clicking on the" + " button below.", ) self.assertIn( - '

    We inform you that the password for your account tester will expire' - f' in 7 days, precisely on {expected_date}.

    \n\n

    ', + "

    We inform you that the password for your account tester will expire" + f" in 7 days, precisely on {expected_date}.

    \n\n

    ", email.alternatives[0][0], ) self.assertIn( - 'Kindly proceed with updating your password by clicking on the button' - ' below.

    ', + "Kindly proceed with updating your password by clicking on the button" + " below.

    ", email.alternatives[0][0], ) self.assertNotEqual(email.to, [unverified_email_user.email]) - @patch.object(app_settings, 'USER_PASSWORD_EXPIRATION', 30) - @patch('openwisp_users.tasks.sleep') + @patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 30) + @patch("openwisp_users.tasks.sleep") def test_password_expiration_mail_sleep(self, mocked_sleep): user_expiry_date = now().date() - timedelta( days=(app_settings.USER_PASSWORD_EXPIRATION - 7) ) for i in range(10): - self._create_user(username=f'user{i}', email=f'user{i}@example.com') + self._create_user(username=f"user{i}", email=f"user{i}@example.com") EmailAddress.objects.update(verified=True) self.assertEqual(User.objects.count(), 10) User.objects.update(password_updated=user_expiry_date) @@ -484,8 +484,8 @@ def test_password_expiration_mail_sleep(self, mocked_sleep): self.assertGreaterEqual(mocked_sleep.call_args[0][0], 1) self.assertLessEqual(mocked_sleep.call_args[0][0], 2) - @patch.object(app_settings, 'USER_PASSWORD_EXPIRATION', 0) - @patch.object(app_settings, 'STAFF_USER_PASSWORD_EXPIRATION', 0) + @patch.object(app_settings, "USER_PASSWORD_EXPIRATION", 0) + @patch.object(app_settings, "STAFF_USER_PASSWORD_EXPIRATION", 0) def test_password_expiration_mail_settings_disabled(self): """ Tests that email are not sent when password expiration feature is disabled @@ -494,6 +494,6 @@ def test_password_expiration_mail_settings_disabled(self): self.assertEqual(app_settings.STAFF_USER_PASSWORD_EXPIRATION, 0) self._create_user() User.objects.update(password_updated=now().date() - timedelta(days=180)) - with patch('openwisp_utils.admin_theme.email.send_email') as mocked_send_email: + with patch("openwisp_utils.admin_theme.email.send_email") as mocked_send_email: password_expiration_email.delay() mocked_send_email.assert_not_called() diff --git a/openwisp_users/tests/utils.py b/openwisp_users/tests/utils.py index efd224c53..6c8bbe27b 100644 --- a/openwisp_users/tests/utils.py +++ b/openwisp_users/tests/utils.py @@ -5,10 +5,10 @@ from django.urls import reverse from swapper import load_model -Organization = load_model('openwisp_users', 'Organization') -OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner') -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') -Group = load_model('openwisp_users', 'Group') +Organization = load_model("openwisp_users", "Organization") +OrganizationOwner = load_model("openwisp_users", "OrganizationOwner") +OrganizationUser = load_model("openwisp_users", "OrganizationUser") +Group = load_model("openwisp_users", "Group") User = get_user_model() @@ -32,11 +32,11 @@ def _additional_params_add(self): class TestOrganizationMixin(object): def _create_user(self, **kwargs): opts = dict( - username='tester', - password='tester', - first_name='Tester', - last_name='Tester', - email='test@tester.com', + username="tester", + password="tester", + first_name="Tester", + last_name="Tester", + email="test@tester.com", birth_date=date(1987, 3, 23), ) opts.update(kwargs) @@ -53,13 +53,13 @@ def _create_admin(self, **kwargs): a staff user with administrator group. """ opts = dict( - username='admin', email='admin@admin.com', is_superuser=True, is_staff=True + username="admin", email="admin@admin.com", is_superuser=True, is_staff=True ) opts.update(kwargs) return self._create_user(**opts) def _create_org(self, **kwargs): - options = {'name': 'test org', 'is_active': True, 'slug': 'test-org'} + options = {"name": "test org", "is_active": True, "slug": "test-org"} options.update(kwargs) org = Organization.objects.create(**options) return org @@ -70,7 +70,7 @@ def _create_operator_with_user_permissions(self, organizations=[], **kwargs): additional privileges to manage users """ operator = self._create_operator(organizations, **kwargs) - user_permissions = Permission.objects.filter(codename__endswith='user') + user_permissions = Permission.objects.filter(codename__endswith="user") operator.user_permissions.add(*user_permissions) operator.organizations_dict # force caching return operator @@ -80,15 +80,15 @@ def _create_operator(self, organizations=[], **kwargs): Creates a staff user with the operator group """ opts = dict( - username='operator', - password='tester', - email='operator@test.com', + username="operator", + password="tester", + email="operator@test.com", is_staff=True, birth_date=date(1987, 3, 23), ) opts.update(kwargs) operator = User.objects.create_user(**opts) - groups = Group.objects.filter(name='Operator') + groups = Group.objects.filter(name="Operator") operator.groups.set(groups) for organization in organizations: OrganizationUser.objects.create( @@ -102,14 +102,14 @@ def _create_administrator(self, organizations=[], **kwargs): Creates a staff user with the administrator group """ opts = dict( - username='administrator', - password='tester', - email='administrator@test.com', + username="administrator", + password="tester", + email="administrator@test.com", is_staff=True, ) opts.update(kwargs) administrator = User.objects.create_user(**opts) - groups = Group.objects.filter(name='Administrator') + groups = Group.objects.filter(name="Administrator") administrator.groups.set(groups) for organization in organizations: OrganizationUser.objects.create( @@ -118,25 +118,25 @@ def _create_administrator(self, organizations=[], **kwargs): administrator.organizations_dict # force caching return administrator - def _get_org(self, org_name='test org'): + def _get_org(self, org_name="test org"): try: return Organization.objects.get(name=org_name) except Organization.DoesNotExist: return self._create_org(name=org_name) - def _get_user(self, username='tester'): + def _get_user(self, username="tester"): try: return User.objects.get(username=username) except User.DoesNotExist: return self._create_user() - def _get_admin(self, username='admin'): + def _get_admin(self, username="admin"): try: return User.objects.get(username=username) except User.DoesNotExist: return self._create_admin() - def _get_operator(self, username='operator'): + def _get_operator(self, username="operator"): try: return User.objects.get(username=username) except User.DoesNotExist: @@ -144,9 +144,9 @@ def _get_operator(self, username='operator'): def _create_org_user(self, **kwargs): options = { - 'organization': self._get_org(), - 'is_admin': False, - 'user': self._get_user(), + "organization": self._get_org(), + "is_admin": False, + "user": self._get_user(), } options.update(kwargs) org = OrganizationUser.objects.create(**options) @@ -162,8 +162,8 @@ def _get_org_user(self): def _create_org_owner(self, **kwargs): options = { - 'organization_user': self._get_org_user(), - 'organization': self._get_org(), + "organization_user": self._get_org_user(), + "organization": self._get_org(), } options.update(kwargs) org_owner = OrganizationOwner.objects.create(**options) @@ -172,10 +172,10 @@ def _create_org_owner(self, **kwargs): class TestMultitenantAdminMixin(TestOrganizationMixin): def setUp(self): - admin = self._create_admin(password='tester') + admin = self._create_admin(password="tester") admin.organizations_dict # force caching - def _login(self, username='admin', password='tester'): + def _login(self, username="admin", password="tester"): self.client.login(username=username, password=password) def _logout(self): @@ -192,22 +192,22 @@ def _test_multitenant_admin( a superuser can see everything. """ if administrator: - self._login(username='administrator', password='tester') + self._login(username="administrator", password="tester") else: - self._login(username='operator', password='tester') + self._login(username="operator", password="tester") response = self.client.get(url) # utility format function def _f(el, select_widget=False): if select_widget: - return '{0}'.format(el) + return "{0}".format(el) return el # ensure elements in visible list are visible to operator for el in visible: with self.subTest(el): self.assertContains( - response, _f(el, select_widget), msg_prefix='[operator contains]' + response, _f(el, select_widget), msg_prefix="[operator contains]" ) # ensure elements in hidden list are not visible to operator for el in hidden: @@ -215,30 +215,30 @@ def _f(el, select_widget=False): self.assertNotContains( response, _f(el, select_widget), - msg_prefix='[operator not-contains]', + msg_prefix="[operator not-contains]", ) # now become superuser self._logout() - self._login(username='admin', password='tester') + self._login(username="admin", password="tester") response = self.client.get(url) # ensure all elements are visible to superuser all_elements = visible + hidden for el in all_elements: self.assertContains( - response, _f(el, select_widget), msg_prefix='[superuser contains]' + response, _f(el, select_widget), msg_prefix="[superuser contains]" ) def _test_recoverlist_operator_403(self, app_label, model_label): - self._login(username='operator', password='tester') + self._login(username="operator", password="tester") response = self.client.get( - reverse('admin:{0}_{1}_recoverlist'.format(app_label, model_label)) + reverse("admin:{0}_{1}_recoverlist".format(app_label, model_label)) ) self.assertEqual(response.status_code, 403) def _get_autocomplete_view_path(self, app_label, model_name, field_name): - path = reverse('admin:ow-auto-filter') + path = reverse("admin:ow-auto-filter") return ( - f'{path}?app_label={app_label}' - f'&model_name={model_name}&field_name={field_name}' + f"{path}?app_label={app_label}" + f"&model_name={model_name}&field_name={field_name}" ) diff --git a/openwisp_users/utils.py b/openwisp_users/utils.py index 8441accc3..21ae5acc2 100644 --- a/openwisp_users/utils.py +++ b/openwisp_users/utils.py @@ -1,6 +1,6 @@ from django.conf import settings -if 'reversion' in settings.INSTALLED_APPS: # pragma: no cover +if "reversion" in settings.INSTALLED_APPS: # pragma: no cover from reversion.admin import VersionAdmin as BaseModelAdmin else: from django.contrib.admin import ModelAdmin as BaseModelAdmin @@ -19,13 +19,13 @@ def usermodel_add_form(model, additional_fields): for field in additional_fields: modelMeta = model.add_form.Meta # Add form fieldsets - add_fieldsets = modelMeta.fieldsets[0][1]['fields'][:] - modelMeta.fieldsets[0][1]['fields'] = ( + add_fieldsets = modelMeta.fieldsets[0][1]["fields"][:] + modelMeta.fieldsets[0][1]["fields"] = ( add_fieldsets[: field[0]] + [field[1]] + add_fieldsets[field[0] :] ) # Add form fieldsets_superuser - add_fieldsets_superuser = modelMeta.fieldsets_superuser[0][1]['fields'][:] - modelMeta.fieldsets_superuser[0][1]['fields'] = ( + add_fieldsets_superuser = modelMeta.fieldsets_superuser[0][1]["fields"][:] + modelMeta.fieldsets_superuser[0][1]["fields"] = ( add_fieldsets_superuser[: field[0]] + [field[1]] + add_fieldsets_superuser[field[0] :] @@ -40,8 +40,8 @@ def usermodel_change_form(model, additional_fields): # Change form fieldsets for field in additional_fields: - fieldsets = model.fieldsets[1][1]['fields'][:] - model.fieldsets[1][1]['fields'] = ( + fieldsets = model.fieldsets[1][1]["fields"][:] + model.fieldsets[1][1]["fields"] = ( fieldsets[: field[0]] + [field[1]] + fieldsets[field[0] :] ) diff --git a/openwisp_users/views.py b/openwisp_users/views.py index 34cebd22a..5fbb9c757 100644 --- a/openwisp_users/views.py +++ b/openwisp_users/views.py @@ -6,7 +6,7 @@ from .widgets import SHARED_SYSTEMWIDE_LABEL -Organization = load_model('openwisp_users', 'Organization') +Organization = load_model("openwisp_users", "Organization") class AutocompleteJsonView(BaseAutocompleteJsonView): @@ -21,11 +21,11 @@ def get_source_field_filter(self): except KeyError: return None for filter in source_model_admin.list_filter: - if getattr(filter, 'field_name', None) == self.source_field.name: + if getattr(filter, "field_name", None) == self.source_field.name: return filter def get_org_lookup(self): - if hasattr(self.source_field_filter, 'org_lookup'): + if hasattr(self.source_field_filter, "org_lookup"): return self.source_field_filter.org_lookup def get_queryset(self): diff --git a/openwisp_users/widgets.py b/openwisp_users/widgets.py index 374fe5b8b..3b23fed67 100644 --- a/openwisp_users/widgets.py +++ b/openwisp_users/widgets.py @@ -2,23 +2,23 @@ from django.urls import reverse from django.utils.translation import gettext_lazy as _ -SHARED_SYSTEMWIDE_LABEL = _('Shared systemwide (no organization)') +SHARED_SYSTEMWIDE_LABEL = _("Shared systemwide (no organization)") class OrganizationAutocompleteSelect(BaseAutocompleteSelect): class Media: - js = ['admin/js/jquery.init.js', 'openwisp-users/js/org-autocomplete.js'] + js = ["admin/js/jquery.init.js", "openwisp-users/js/org-autocomplete.js"] def get_url(self): - return reverse('admin:ow-auto-filter') + return reverse("admin:ow-auto-filter") def optgroups(self, name, value, attrs=None): groups = super().optgroups(name, value, attrs) - if value == [''] and len(groups[0][1]) == 1: + if value == [""] and len(groups[0][1]) == 1: groups[0][1].append( self.create_option( name=name, - value='null', + value="null", label=SHARED_SYSTEMWIDE_LABEL, selected=False, index=2, diff --git a/runtests.py b/runtests.py index b699a64f1..dce88d479 100755 --- a/runtests.py +++ b/runtests.py @@ -4,18 +4,18 @@ import os import sys -sys.path.insert(0, 'tests') -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openwisp2.settings') +sys.path.insert(0, "tests") +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openwisp2.settings") -if __name__ == '__main__': +if __name__ == "__main__": from django.core.management import execute_from_command_line args = sys.argv - args.insert(1, 'test') - if not os.environ.get('SAMPLE_APP', False): - args.insert(2, 'openwisp_users') - args.insert(3, 'testapp.tests') + args.insert(1, "test") + if not os.environ.get("SAMPLE_APP", False): + args.insert(2, "openwisp_users") + args.insert(3, "testapp.tests") else: - args.insert(2, 'openwisp2') - args.insert(3, 'testapp.tests') + args.insert(2, "openwisp2") + args.insert(3, "testapp.tests") execute_from_command_line(args) diff --git a/setup.py b/setup.py index 5bb083b6b..a445f75fb 100644 --- a/setup.py +++ b/setup.py @@ -12,13 +12,13 @@ def get_install_requires(): parse requirements.txt, ignore links, exclude comments """ requirements = [] - for line in open('requirements.txt').readlines(): + for line in open("requirements.txt").readlines(): # skip to next iteration if comment or empty line if ( - line.startswith('#') - or line == '' - or line.startswith('http') - or line.startswith('git') + line.startswith("#") + or line == "" + or line.startswith("http") + or line.startswith("git") ): continue # add line to requirements @@ -26,13 +26,13 @@ def get_install_requires(): return requirements -if sys.argv[-1] == 'publish': +if sys.argv[-1] == "publish": # delete any *.pyc, *.pyo and __pycache__ os.system('find . | grep -E "(__pycache__|\.pyc|\.pyo$)" | xargs rm -rf') os.system("python setup.py sdist bdist_wheel") os.system("twine upload -s dist/*") os.system("rm -rf dist build") - args = {'version': get_version()} + args = {"version": get_version()} print("You probably want to also tag the version now:") print(" git tag -a %(version)s -m 'version %(version)s'" % args) print(" git push --tags") @@ -40,29 +40,29 @@ def get_install_requires(): setup( - name='openwisp-users', + name="openwisp-users", version=get_version(), - license='BSD', - author='OpenWISP', - author_email='support@openwisp.io', - description='Provides basic multi-tenancy features for OpenWISP (using the Django web-framework)', - long_description=open('README.rst').read(), - url='http://openwisp.org', - download_url='https://github.com/openwisp/openwisp-users/releases', - platforms=['Platform Independent'], - keywords=['openwisp', 'django', 'organizations', 'users'], - packages=find_packages(exclude=['tests*', 'docs*']), + license="BSD", + author="OpenWISP", + author_email="support@openwisp.io", + description="Provides basic multi-tenancy features for OpenWISP (using the Django web-framework)", + long_description=open("README.rst").read(), + url="http://openwisp.org", + download_url="https://github.com/openwisp/openwisp-users/releases", + platforms=["Platform Independent"], + keywords=["openwisp", "django", "organizations", "users"], + packages=find_packages(exclude=["tests*", "docs*"]), include_package_data=True, zip_safe=False, install_requires=get_install_requires(), classifiers=[ - 'Development Status :: 5 - Production/Stable', - 'Environment :: Web Environment', - 'Topic :: Internet :: WWW/HTTP', - 'Intended Audience :: Developers', - 'License :: OSI Approved :: BSD License', - 'Operating System :: OS Independent', - 'Framework :: Django', - 'Programming Language :: Python :: 3', + "Development Status :: 5 - Production/Stable", + "Environment :: Web Environment", + "Topic :: Internet :: WWW/HTTP", + "Intended Audience :: Developers", + "License :: OSI Approved :: BSD License", + "Operating System :: OS Independent", + "Framework :: Django", + "Programming Language :: Python :: 3", ], ) diff --git a/tests/manage.py b/tests/manage.py index be1b9ff3f..33876c7bf 100755 --- a/tests/manage.py +++ b/tests/manage.py @@ -2,8 +2,8 @@ import os import sys -if __name__ == '__main__': - os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openwisp2.settings') +if __name__ == "__main__": + os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openwisp2.settings") from django.core.management import execute_from_command_line diff --git a/tests/openwisp2/__init__.py b/tests/openwisp2/__init__.py index cd0426407..370372af0 100644 --- a/tests/openwisp2/__init__.py +++ b/tests/openwisp2/__init__.py @@ -1,3 +1,3 @@ from .celery import app as celery_app -__all__ = ['celery_app'] +__all__ = ["celery_app"] diff --git a/tests/openwisp2/celery.py b/tests/openwisp2/celery.py index e3c1425d4..311028e83 100644 --- a/tests/openwisp2/celery.py +++ b/tests/openwisp2/celery.py @@ -2,8 +2,8 @@ from celery import Celery -os.environ.setdefault('DJANGO_SETTINGS_MODULE', 'openwisp2.settings') +os.environ.setdefault("DJANGO_SETTINGS_MODULE", "openwisp2.settings") -app = Celery('openwisp2') -app.config_from_object('django.conf:settings', namespace='CELERY') +app = Celery("openwisp2") +app.config_from_object("django.conf:settings", namespace="CELERY") app.autodiscover_tasks() diff --git a/tests/openwisp2/sample_users/admin.py b/tests/openwisp2/sample_users/admin.py index ad0c180ca..47c0bf13e 100644 --- a/tests/openwisp2/sample_users/admin.py +++ b/tests/openwisp2/sample_users/admin.py @@ -8,7 +8,7 @@ # Fields to be added in the UserAdmin additional_fields = [ # [position-of-field-in-form, name-of-field] - [2, 'social_security_number'] + [2, "social_security_number"] ] # Add field to the User sign up form diff --git a/tests/openwisp2/sample_users/apps.py b/tests/openwisp2/sample_users/apps.py index 4e3882404..397da75b2 100644 --- a/tests/openwisp2/sample_users/apps.py +++ b/tests/openwisp2/sample_users/apps.py @@ -2,8 +2,8 @@ class SampleUsersConfig(OpenwispUsersConfig): - name = 'openwisp2.sample_users' - label = 'sample_users' + name = "openwisp2.sample_users" + label = "sample_users" del OpenwispUsersConfig diff --git a/tests/openwisp2/sample_users/migrations/0001_initial.py b/tests/openwisp2/sample_users/migrations/0001_initial.py index 30abb11c6..f8152c2f8 100644 --- a/tests/openwisp2/sample_users/migrations/0001_initial.py +++ b/tests/openwisp2/sample_users/migrations/0001_initial.py @@ -19,79 +19,79 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('auth', '0011_update_proxy_permissions'), + ("auth", "0011_update_proxy_permissions"), ] operations = [ migrations.CreateModel( - name='User', + name="User", fields=[ - ('password', models.CharField(max_length=128, verbose_name='password')), + ("password", models.CharField(max_length=128, verbose_name="password")), ( - 'last_login', + "last_login", models.DateTimeField( - blank=True, null=True, verbose_name='last login' + blank=True, null=True, verbose_name="last login" ), ), ( - 'is_superuser', + "is_superuser", models.BooleanField( default=False, - help_text='Designates that this user has all permissions without explicitly assigning them.', - verbose_name='superuser status', + help_text="Designates that this user has all permissions without explicitly assigning them.", + verbose_name="superuser status", ), ), ( - 'username', + "username", models.CharField( error_messages={ - 'unique': 'A user with that username already exists.' + "unique": "A user with that username already exists." }, - help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', + help_text="Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.", max_length=150, unique=True, validators=[ django.contrib.auth.validators.UnicodeUsernameValidator() ], - verbose_name='username', + verbose_name="username", ), ), ( - 'first_name', + "first_name", models.CharField( - blank=True, max_length=150, verbose_name='first name' + blank=True, max_length=150, verbose_name="first name" ), ), ( - 'last_name', + "last_name", models.CharField( - blank=True, max_length=150, verbose_name='last name' + blank=True, max_length=150, verbose_name="last name" ), ), ( - 'is_staff', + "is_staff", models.BooleanField( default=False, - help_text='Designates whether the user can log into this admin site.', - verbose_name='staff status', + help_text="Designates whether the user can log into this admin site.", + verbose_name="staff status", ), ), ( - 'is_active', + "is_active", models.BooleanField( default=True, - help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', - verbose_name='active', + help_text="Designates whether this user should be treated as active. Unselect this instead of deleting accounts.", + verbose_name="active", ), ), ( - 'date_joined', + "date_joined", models.DateTimeField( - default=django.utils.timezone.now, verbose_name='date joined' + default=django.utils.timezone.now, verbose_name="date joined" ), ), ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -100,88 +100,88 @@ class Migration(migrations.Migration): ), ), ( - 'email', + "email", models.EmailField( blank=True, max_length=254, null=True, unique=True, - verbose_name='email address', + verbose_name="email address", ), ), - ('bio', models.TextField(blank=True, verbose_name='bio')), - ('url', models.URLField(blank=True, verbose_name='URL')), + ("bio", models.TextField(blank=True, verbose_name="bio")), + ("url", models.URLField(blank=True, verbose_name="URL")), ( - 'company', - models.CharField(blank=True, max_length=30, verbose_name='company'), + "company", + models.CharField(blank=True, max_length=30, verbose_name="company"), ), ( - 'location', + "location", models.CharField( - blank=True, max_length=256, verbose_name='location' + blank=True, max_length=256, verbose_name="location" ), ), ( - 'phone_number', + "phone_number", phonenumber_field.modelfields.PhoneNumberField( blank=True, max_length=128, null=True, region=None, unique=True, - verbose_name='phone number', + verbose_name="phone number", ), ), ( - 'birth_date', - models.DateField(blank=True, null=True, verbose_name='birth date'), + "birth_date", + models.DateField(blank=True, null=True, verbose_name="birth date"), ), ( - 'notes', + "notes", models.TextField( blank=True, - help_text='notes for internal usage', - verbose_name='notes', + help_text="notes for internal usage", + verbose_name="notes", ), ), - ('details', models.CharField(blank=True, max_length=64, null=True)), + ("details", models.CharField(blank=True, max_length=64, null=True)), ( - 'social_security_number', + "social_security_number", models.CharField( blank=True, max_length=11, null=True, validators=[ django.core.validators.RegexValidator( - '^\\d\\d\\d-\\d\\d-\\d\\d\\d\\d$' + "^\\d\\d\\d-\\d\\d-\\d\\d\\d\\d$" ) ], ), ), ( - 'groups', + "groups", models.ManyToManyField( blank=True, - help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', - related_name='user_set', - related_query_name='user', - to='auth.Group', - verbose_name='groups', + help_text="The groups this user belongs to. A user will get all permissions granted to each of their groups.", + related_name="user_set", + related_query_name="user", + to="auth.Group", + verbose_name="groups", ), ), ( - 'user_permissions', + "user_permissions", models.ManyToManyField( blank=True, - help_text='Specific permissions for this user.', - related_name='user_set', - related_query_name='user', - to='auth.Permission', - verbose_name='user permissions', + help_text="Specific permissions for this user.", + related_name="user_set", + related_query_name="user", + to="auth.Permission", + verbose_name="user permissions", ), ), ( - 'language', + "language", models.CharField( choices=settings.LANGUAGES, default=settings.LANGUAGE_CODE, @@ -189,86 +189,86 @@ class Migration(migrations.Migration): ), ), ( - 'password_updated', + "password_updated", models.DateField( blank=True, null=True, verbose_name="password updated" ), ), ], options={ - 'verbose_name': 'user', - 'verbose_name_plural': 'users', - 'abstract': False, - 'indexes': [ + "verbose_name": "user", + "verbose_name_plural": "users", + "abstract": False, + "indexes": [ models.Index( - fields=['id', 'email'], - name='user_id_email_idx', + fields=["id", "email"], + name="user_id_email_idx", ) ], }, managers=[ - ('objects', openwisp_users.base.models.UserManager()), + ("objects", openwisp_users.base.models.UserManager()), ], ), migrations.CreateModel( - name='Group', + name="Group", fields=[ ( - 'group_ptr', + "group_ptr", models.OneToOneField( auto_created=True, on_delete=django.db.models.deletion.CASCADE, parent_link=True, primary_key=True, serialize=False, - to='auth.group', + to="auth.group", ), ), - ('details', models.CharField(blank=True, max_length=64, null=True)), + ("details", models.CharField(blank=True, max_length=64, null=True)), ], options={ - 'abstract': False, + "abstract": False, }, - bases=(openwisp_users.base.models.BaseGroup, 'auth.group', models.Model), + bases=(openwisp_users.base.models.BaseGroup, "auth.group", models.Model), managers=[ - ('objects', django.contrib.auth.models.GroupManager()), + ("objects", django.contrib.auth.models.GroupManager()), ], ), migrations.CreateModel( - name='Organization', + name="Organization", fields=[ ( - 'name', + "name", models.CharField( - help_text='The name of the organization', max_length=200 + help_text="The name of the organization", max_length=200 ), ), - ('is_active', models.BooleanField(default=True)), + ("is_active", models.BooleanField(default=True)), ( - 'created', + "created", organizations.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False ), ), ( - 'modified', + "modified", organizations.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False ), ), ( - 'slug', + "slug", organizations.fields.SlugField( blank=True, editable=False, - help_text='The name in all lowercase, suitable for URL identification', + help_text="The name in all lowercase, suitable for URL identification", max_length=200, - populate_from='name', + populate_from="name", unique=True, ), ), ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -277,63 +277,63 @@ class Migration(migrations.Migration): ), ), ( - 'description', - models.TextField(blank=True, verbose_name='description'), + "description", + models.TextField(blank=True, verbose_name="description"), ), ( - 'email', - models.EmailField(blank=True, max_length=254, verbose_name='email'), + "email", + models.EmailField(blank=True, max_length=254, verbose_name="email"), ), - ('url', models.URLField(blank=True, verbose_name='URL')), - ('details', models.CharField(blank=True, max_length=64, null=True)), + ("url", models.URLField(blank=True, verbose_name="URL")), + ("details", models.CharField(blank=True, max_length=64, null=True)), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='UserInlineModel', + name="UserInlineModel", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('details', models.CharField(blank=True, max_length=64, null=True)), + ("details", models.CharField(blank=True, max_length=64, null=True)), ( - 'user', + "user", models.OneToOneField( on_delete=django.db.models.deletion.CASCADE, - to='sample_users.user', + to="sample_users.user", ), ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='OrganizationUser', + name="OrganizationUser", fields=[ ( - 'created', + "created", organizations.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False ), ), ( - 'modified', + "modified", organizations.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False ), ), - ('is_admin', models.BooleanField(default=False)), + ("is_admin", models.BooleanField(default=False)), ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -341,45 +341,45 @@ class Migration(migrations.Migration): serialize=False, ), ), - ('details', models.CharField(blank=True, max_length=64, null=True)), + ("details", models.CharField(blank=True, max_length=64, null=True)), ( - 'organization', + "organization", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='organization_users', - to='sample_users.organization', + related_name="organization_users", + to="sample_users.organization", ), ), ( - 'user', + "user", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='%(app_label)s_%(class)s', - to='sample_users.user', + related_name="%(app_label)s_%(class)s", + to="sample_users.user", ), ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='OrganizationOwner', + name="OrganizationOwner", fields=[ ( - 'created', + "created", organizations.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False ), ), ( - 'modified', + "modified", organizations.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False ), ), ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -387,121 +387,121 @@ class Migration(migrations.Migration): serialize=False, ), ), - ('details', models.CharField(blank=True, max_length=64, null=True)), + ("details", models.CharField(blank=True, max_length=64, null=True)), ( - 'organization', + "organization", models.OneToOneField( on_delete=django.db.models.deletion.CASCADE, - related_name='owner', - to='sample_users.organization', + related_name="owner", + to="sample_users.organization", ), ), ( - 'organization_user', + "organization_user", models.OneToOneField( on_delete=django.db.models.deletion.CASCADE, - to='sample_users.organizationuser', + to="sample_users.organizationuser", ), ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='OrganizationInvitation', + name="OrganizationInvitation", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('guid', models.UUIDField(editable=False)), + ("guid", models.UUIDField(editable=False)), ( - 'invitee_identifier', + "invitee_identifier", models.CharField( - help_text='The contact identifier for the invitee, email, phone number, social media handle, etc.', + help_text="The contact identifier for the invitee, email, phone number, social media handle, etc.", max_length=1000, ), ), ( - 'created', + "created", organizations.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False ), ), ( - 'modified', + "modified", organizations.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False ), ), ( - 'invited_by', + "invited_by", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='%(app_label)s_%(class)s_sent_invitations', - to='sample_users.user', + related_name="%(app_label)s_%(class)s_sent_invitations", + to="sample_users.user", ), ), ( - 'invitee', + "invitee", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - related_name='%(app_label)s_%(class)s_invitations', - to='sample_users.user', + related_name="%(app_label)s_%(class)s_invitations", + to="sample_users.user", ), ), ( - 'organization', + "organization", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - related_name='organization_invites', - to='sample_users.organization', + related_name="organization_invites", + to="sample_users.organization", ), ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.CreateModel( - name='OrganizationInlineModel', + name="OrganizationInlineModel", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('details', models.CharField(blank=True, max_length=64, null=True)), + ("details", models.CharField(blank=True, max_length=64, null=True)), ( - 'organization', + "organization", models.OneToOneField( on_delete=django.db.models.deletion.CASCADE, - to='sample_users.organization', + to="sample_users.organization", ), ), ], options={ - 'abstract': False, + "abstract": False, }, ), migrations.AddField( - model_name='organization', - name='users', + model_name="organization", + name="users", field=models.ManyToManyField( - related_name='%(app_label)s_%(class)s', - through='sample_users.OrganizationUser', + related_name="%(app_label)s_%(class)s", + through="sample_users.OrganizationUser", to=settings.AUTH_USER_MODEL, ), ), diff --git a/tests/openwisp2/sample_users/migrations/0004_default_groups.py b/tests/openwisp2/sample_users/migrations/0004_default_groups.py index b750b7171..b1ab675da 100644 --- a/tests/openwisp2/sample_users/migrations/0004_default_groups.py +++ b/tests/openwisp2/sample_users/migrations/0004_default_groups.py @@ -13,9 +13,9 @@ class Migration(migrations.Migration): - org_model = swapper.get_model_name('openwisp_users', 'organization') + org_model = swapper.get_model_name("openwisp_users", "organization") model_app_label = swapper.split(org_model)[0] - dependencies = [(model_app_label, '0001_initial')] + dependencies = [(model_app_label, "0001_initial")] operations = [ migrations.RunPython( diff --git a/tests/openwisp2/sample_users/models.py b/tests/openwisp2/sample_users/models.py index cfae153d2..f8943efee 100644 --- a/tests/openwisp2/sample_users/models.py +++ b/tests/openwisp2/sample_users/models.py @@ -39,7 +39,7 @@ class User(DetailsModel, AbstractUser): max_length=11, null=True, blank=True, - validators=[RegexValidator(r'^\d\d\d-\d\d-\d\d\d\d$')], + validators=[RegexValidator(r"^\d\d\d-\d\d-\d\d\d\d$")], ) class Meta(AbstractUser.Meta): diff --git a/tests/openwisp2/sample_users/tests.py b/tests/openwisp2/sample_users/tests.py index df45daadd..3f7e03a17 100644 --- a/tests/openwisp2/sample_users/tests.py +++ b/tests/openwisp2/sample_users/tests.py @@ -23,8 +23,8 @@ from openwisp_users.tests.test_models import TestUsers as BaseTestUsers additional_fields = [ - ('social_security_number', '123-45-6789'), - ('details', 'Example value for detail used during testing.'), + ("social_security_number", "123-45-6789"), + ("details", "Example value for detail used during testing."), ] @@ -44,12 +44,12 @@ def _get_org_edit_form_inline_params(self, user, organization): params = super()._get_user_edit_form_inline_params(user, organization) params.update( { - 'organizationinlinemodel-TOTAL_FORMS': 1, - 'organizationinlinemodel-INITIAL_FORMS': 0, - 'organizationinlinemodel-MIN_NUM_FORMS': 0, - 'organizationinlinemodel-MAX_NUM_FORMS': 1, - 'organizationinlinemodel-0-details': '', - 'organizationinlinemodel-0-user': str(organization.pk), + "organizationinlinemodel-TOTAL_FORMS": 1, + "organizationinlinemodel-INITIAL_FORMS": 0, + "organizationinlinemodel-MIN_NUM_FORMS": 0, + "organizationinlinemodel-MAX_NUM_FORMS": 1, + "organizationinlinemodel-0-details": "", + "organizationinlinemodel-0-user": str(organization.pk), } ) return params @@ -63,19 +63,19 @@ def _get_user_edit_form_inline_params(self, user, organization): params = super()._get_user_edit_form_inline_params(user, organization) params.update( { - 'userinlinemodel-TOTAL_FORMS': 1, - 'userinlinemodel-INITIAL_FORMS': 0, - 'userinlinemodel-MIN_NUM_FORMS': 0, - 'userinlinemodel-MAX_NUM_FORMS': 1, - 'userinlinemodel-0-details': '', - 'userinlinemodel-0-user': str(user.pk), + "userinlinemodel-TOTAL_FORMS": 1, + "userinlinemodel-INITIAL_FORMS": 0, + "userinlinemodel-MIN_NUM_FORMS": 0, + "userinlinemodel-MAX_NUM_FORMS": 1, + "userinlinemodel-0-details": "", + "userinlinemodel-0-user": str(user.pk), } ) return params class TestUsersAdmin(GetEditFormInlineMixin, BaseTestUsersAdmin): - app_label = 'sample_users' + app_label = "sample_users" _additional_user_fields = additional_fields @@ -84,12 +84,12 @@ class TestUserPasswordExpiration(BaseTestUserPasswordExpiration): class TestBasicUsersIntegration(GetEditFormInlineMixin, BaseTestBasicUsersIntegration): - app_label = 'sample_users' + app_label = "sample_users" _additional_user_fields = additional_fields class TestMultitenantAdmin(BaseTestMultitenantAdmin): - app_label = 'sample_users' + app_label = "sample_users" class TestUsers(BaseTestUsers): diff --git a/tests/openwisp2/settings.py b/tests/openwisp2/settings.py index 05f85073c..1cefe27fc 100644 --- a/tests/openwisp2/settings.py +++ b/tests/openwisp2/settings.py @@ -5,170 +5,170 @@ BASE_DIR = os.path.dirname(os.path.abspath(__file__)) DEBUG = True -TESTING = sys.argv[1] == 'test' -PARALLEL = '--parallel' in sys.argv -SHELL = 'shell' in sys.argv or 'shell_plus' in sys.argv -SAMPLE_APP = os.environ.get('SAMPLE_APP', False) +TESTING = sys.argv[1] == "test" +PARALLEL = "--parallel" in sys.argv +SHELL = "shell" in sys.argv or "shell_plus" in sys.argv +SAMPLE_APP = os.environ.get("SAMPLE_APP", False) -ALLOWED_HOSTS = ['*'] +ALLOWED_HOSTS = ["*"] DATABASES = { - 'default': { - 'ENGINE': 'django.db.backends.sqlite3', - 'NAME': 'openwisp-users.db' - if not SAMPLE_APP - else 'openwisp-users-SAMPLE_APP.db', + "default": { + "ENGINE": "django.db.backends.sqlite3", + "NAME": ( + "openwisp-users.db" if not SAMPLE_APP else "openwisp-users-SAMPLE_APP.db" + ), } } -SECRET_KEY = 'fn)t*+$)ugeyip6-#txyy$5wf2ervc0d2n#h)qb)y5@ly$t*@w' +SECRET_KEY = "fn)t*+$)ugeyip6-#txyy$5wf2ervc0d2n#h)qb)y5@ly$t*@w" INSTALLED_APPS = [ - 'django.contrib.auth', - 'django.contrib.contenttypes', - 'django.contrib.sessions', - 'django.contrib.messages', - 'django.contrib.staticfiles', + "django.contrib.auth", + "django.contrib.contenttypes", + "django.contrib.sessions", + "django.contrib.messages", + "django.contrib.staticfiles", # overrides some templates in django-allauth - 'openwisp_users.accounts', - 'django_extensions', - 'allauth', - 'allauth.account', - 'allauth.socialaccount', - 'openwisp_users', + "openwisp_users.accounts", + "django_extensions", + "allauth", + "allauth.account", + "allauth.socialaccount", + "openwisp_users", # openwisp2 admin theme # (must be loaded here) - 'openwisp_utils.admin_theme', + "openwisp_utils.admin_theme", # must come before the django admin # to override the admin login page - 'rest_framework', - 'rest_framework.authtoken', - 'django.contrib.admin', - 'admin_auto_filters', - 'django.contrib.sites', - 'drf_yasg', - 'testapp', - 'reversion', - 'django_filters', + "rest_framework", + "rest_framework.authtoken", + "django.contrib.admin", + "admin_auto_filters", + "django.contrib.sites", + "drf_yasg", + "testapp", + "reversion", + "django_filters", ] -AUTH_USER_MODEL = 'openwisp_users.User' +AUTH_USER_MODEL = "openwisp_users.User" SITE_ID = 1 STATICFILES_FINDERS = [ - 'django.contrib.staticfiles.finders.FileSystemFinder', - 'django.contrib.staticfiles.finders.AppDirectoriesFinder', - 'openwisp_utils.staticfiles.DependencyFinder', + "django.contrib.staticfiles.finders.FileSystemFinder", + "django.contrib.staticfiles.finders.AppDirectoriesFinder", + "openwisp_utils.staticfiles.DependencyFinder", ] MIDDLEWARE = [ - 'django.middleware.security.SecurityMiddleware', - 'django.contrib.sessions.middleware.SessionMiddleware', - 'django.middleware.common.CommonMiddleware', - 'django.middleware.csrf.CsrfViewMiddleware', - 'django.contrib.auth.middleware.AuthenticationMiddleware', - 'allauth.account.middleware.AccountMiddleware', - 'django.contrib.messages.middleware.MessageMiddleware', - 'django.middleware.clickjacking.XFrameOptionsMiddleware', - 'openwisp_users.middleware.PasswordExpirationMiddleware', + "django.middleware.security.SecurityMiddleware", + "django.contrib.sessions.middleware.SessionMiddleware", + "django.middleware.common.CommonMiddleware", + "django.middleware.csrf.CsrfViewMiddleware", + "django.contrib.auth.middleware.AuthenticationMiddleware", + "allauth.account.middleware.AccountMiddleware", + "django.contrib.messages.middleware.MessageMiddleware", + "django.middleware.clickjacking.XFrameOptionsMiddleware", + "openwisp_users.middleware.PasswordExpirationMiddleware", ] AUTH_PASSWORD_VALIDATORS = [ - {'NAME': 'openwisp_users.password_validation.PasswordReuseValidator'} + {"NAME": "openwisp_users.password_validation.PasswordReuseValidator"} ] -ROOT_URLCONF = 'openwisp2.urls' +ROOT_URLCONF = "openwisp2.urls" -TIME_ZONE = 'Europe/Rome' -LANGUAGE_CODE = 'en-gb' +TIME_ZONE = "Europe/Rome" +LANGUAGE_CODE = "en-gb" USE_TZ = True USE_I18N = False USE_L10N = False -STATIC_URL = '/static/' +STATIC_URL = "/static/" TEMPLATES = [ { - 'BACKEND': 'django.template.backends.django.DjangoTemplates', - 'DIRS': [ - os.path.join(os.path.dirname(BASE_DIR), 'openwisp_users', 'templates') + "BACKEND": "django.template.backends.django.DjangoTemplates", + "DIRS": [ + os.path.join(os.path.dirname(BASE_DIR), "openwisp_users", "templates") ], - 'OPTIONS': { - 'loaders': [ - 'django.template.loaders.filesystem.Loader', - 'openwisp_utils.loaders.DependencyLoader', - 'django.template.loaders.app_directories.Loader', + "OPTIONS": { + "loaders": [ + "django.template.loaders.filesystem.Loader", + "openwisp_utils.loaders.DependencyLoader", + "django.template.loaders.app_directories.Loader", ], - 'context_processors': [ - 'django.template.context_processors.debug', - 'django.template.context_processors.request', - 'django.contrib.auth.context_processors.auth', - 'django.contrib.messages.context_processors.messages', - 'openwisp_utils.admin_theme.context_processor.menu_groups', + "context_processors": [ + "django.template.context_processors.debug", + "django.template.context_processors.request", + "django.contrib.auth.context_processors.auth", + "django.contrib.messages.context_processors.messages", + "openwisp_utils.admin_theme.context_processor.menu_groups", ], }, } ] AUTHENTICATION_BACKENDS = [ - 'openwisp_users.backends.UsersAuthenticationBackend', + "openwisp_users.backends.UsersAuthenticationBackend", ] if not TESTING and SHELL: - INSTALLED_APPS.remove('reversion') + INSTALLED_APPS.remove("reversion") LOGGING = { - 'disable_existing_loggers': False, - 'version': 1, - 'handlers': { - 'console': { + "disable_existing_loggers": False, + "version": 1, + "handlers": { + "console": { # logging handler that outputs log messages to terminal - 'class': 'logging.StreamHandler', - 'level': 'DEBUG', # message level to be written to console + "class": "logging.StreamHandler", + "level": "DEBUG", # message level to be written to console } }, - 'loggers': { - '': { + "loggers": { + "": { # this sets root level logger to log debug and higher level # logs to console. All other loggers inherit settings from # root level logger. - 'handlers': ['console'], - 'level': 'DEBUG', - 'propagate': False, + "handlers": ["console"], + "level": "DEBUG", + "propagate": False, }, - 'django.db': { - 'level': 'DEBUG', - 'handlers': ['console'], - 'propagate': False, + "django.db": { + "level": "DEBUG", + "handlers": ["console"], + "propagate": False, }, }, } if not TESTING: - CELERY_BROKER_URL = 'redis://localhost/6' + CELERY_BROKER_URL = "redis://localhost/6" else: CELERY_TASK_ALWAYS_EAGER = True CELERY_TASK_EAGER_PROPAGATES = True - CELERY_BROKER_URL = 'memory://' + CELERY_BROKER_URL = "memory://" CELERY_BEAT_SCHEDULE = { - 'password_expiry_email': { - 'task': 'openwisp_users.tasks.password_expiration_email', - 'schedule': crontab(hour=1, minute=0), + "password_expiry_email": { + "task": "openwisp_users.tasks.password_expiration_email", + "schedule": crontab(hour=1, minute=0), }, } # during development only -EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' -LOGIN_REDIRECT_URL = 'admin:index' -ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = 'email_confirmation_success' -ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = 'email_confirmation_success' +EMAIL_BACKEND = "django.core.mail.backends.console.EmailBackend" +LOGIN_REDIRECT_URL = "admin:index" +ACCOUNT_EMAIL_CONFIRMATION_ANONYMOUS_REDIRECT_URL = "email_confirmation_success" +ACCOUNT_EMAIL_CONFIRMATION_AUTHENTICATED_REDIRECT_URL = "email_confirmation_success" if not PARALLEL: CACHES = { - 'default': { - 'BACKEND': 'django_redis.cache.RedisCache', - 'LOCATION': 'redis://localhost/0', - 'OPTIONS': {'CLIENT_CLASS': 'django_redis.client.DefaultClient'}, + "default": { + "BACKEND": "django_redis.cache.RedisCache", + "LOCATION": "redis://localhost/0", + "OPTIONS": {"CLIENT_CLASS": "django_redis.client.DefaultClient"}, } } # parallel testing with redis cache does not work @@ -177,29 +177,29 @@ # to avoid having bad surprises in production else: CACHES = { - 'default': { - 'BACKEND': 'django.core.cache.backends.locmem.LocMemCache', - 'LOCATION': 'openwisp-users', + "default": { + "BACKEND": "django.core.cache.backends.locmem.LocMemCache", + "LOCATION": "openwisp-users", } } -SESSION_ENGINE = 'django.contrib.sessions.backends.cache' -SESSION_CACHE_ALIAS = 'default' +SESSION_ENGINE = "django.contrib.sessions.backends.cache" +SESSION_CACHE_ALIAS = "default" if SAMPLE_APP: - users_index = INSTALLED_APPS.index('openwisp_users') - INSTALLED_APPS.insert(users_index, 'openwisp2.sample_users') - INSTALLED_APPS.remove('openwisp_users') - EXTENDED_APPS = ['openwisp_users'] - AUTH_USER_MODEL = 'sample_users.User' - OPENWISP_USERS_GROUP_MODEL = 'sample_users.Group' - OPENWISP_USERS_ORGANIZATION_MODEL = 'sample_users.Organization' - OPENWISP_USERS_ORGANIZATIONUSER_MODEL = 'sample_users.OrganizationUser' - OPENWISP_USERS_ORGANIZATIONOWNER_MODEL = 'sample_users.OrganizationOwner' - OPENWISP_USERS_ORGANIZATIONINVITATION_MODEL = 'sample_users.OrganizationInvitation' - -if os.environ.get('NO_SOCIAL_APP', False): - INSTALLED_APPS.remove('allauth.socialaccount') + users_index = INSTALLED_APPS.index("openwisp_users") + INSTALLED_APPS.insert(users_index, "openwisp2.sample_users") + INSTALLED_APPS.remove("openwisp_users") + EXTENDED_APPS = ["openwisp_users"] + AUTH_USER_MODEL = "sample_users.User" + OPENWISP_USERS_GROUP_MODEL = "sample_users.Group" + OPENWISP_USERS_ORGANIZATION_MODEL = "sample_users.Organization" + OPENWISP_USERS_ORGANIZATIONUSER_MODEL = "sample_users.OrganizationUser" + OPENWISP_USERS_ORGANIZATIONOWNER_MODEL = "sample_users.OrganizationOwner" + OPENWISP_USERS_ORGANIZATIONINVITATION_MODEL = "sample_users.OrganizationInvitation" + +if os.environ.get("NO_SOCIAL_APP", False): + INSTALLED_APPS.remove("allauth.socialaccount") # local settings must be imported before test runner otherwise they'll be ignored try: @@ -210,4 +210,4 @@ # Added for silencing warnings raised by django-all-auth # on Django 3.2 and above. Remove when new version of # django-all-auth is released -SILENCED_SYSTEM_CHECKS = ['models.W042'] +SILENCED_SYSTEM_CHECKS = ["models.W042"] diff --git a/tests/openwisp2/urls.py b/tests/openwisp2/urls.py index b1a4ad07c..a9b1571f1 100644 --- a/tests/openwisp2/urls.py +++ b/tests/openwisp2/urls.py @@ -6,7 +6,7 @@ from openwisp_users.api.urls import get_api_urls -if os.environ.get('SAMPLE_APP', False): +if os.environ.get("SAMPLE_APP", False): # We don't need to set any value for api_views # if we don't want to extend the views (optional). api_views = None @@ -15,12 +15,12 @@ from .sample_users import views as api_views urlpatterns = [ - path('admin/', admin.site.urls), - path('accounts/', include('openwisp_users.accounts.urls')), - path('api/v1/', include((get_api_urls(api_views), 'users'), namespace='users')), - path('api/v1/', include('openwisp_utils.api.urls')), + path("admin/", admin.site.urls), + path("accounts/", include("openwisp_users.accounts.urls")), + path("api/v1/", include((get_api_urls(api_views), "users"), namespace="users")), + path("api/v1/", include("openwisp_utils.api.urls")), # Only for testing 'testapp' - path('testing/', include('testapp.urls')), + path("testing/", include("testapp.urls")), ] urlpatterns += staticfiles_urlpatterns() diff --git a/tests/testapp/__init__.py b/tests/testapp/__init__.py index 2be056e99..823fc7517 100644 --- a/tests/testapp/__init__.py +++ b/tests/testapp/__init__.py @@ -1,6 +1,6 @@ class CreateMixin(object): def _create_book(self, **kwargs): - options = dict(name='test-book', author='test-author') + options = dict(name="test-book", author="test-author") options.update(kwargs) b = self.book_model(**options) b.full_clean() @@ -8,7 +8,7 @@ def _create_book(self, **kwargs): return b def _create_shelf(self, **kwargs): - options = dict(name='test-shelf') + options = dict(name="test-shelf") options.update(kwargs) s = self.shelf_model(**options) s.full_clean() @@ -16,7 +16,7 @@ def _create_shelf(self, **kwargs): return s def _create_template(self, **kwargs): - options = dict(name='test-template') + options = dict(name="test-template") options.update(kwargs) t = self.template_model(**options) t.full_clean() @@ -24,7 +24,7 @@ def _create_template(self, **kwargs): return t def _create_library(self, **kwargs): - options = dict(name='test-library', address='test-address') + options = dict(name="test-library", address="test-address") options.update(kwargs) lib = self.library_model(**options) lib.full_clean() @@ -32,7 +32,7 @@ def _create_library(self, **kwargs): return lib def _create_tag(self, **kwargs): - options = dict(name='test-tag') + options = dict(name="test-tag") options.update(kwargs) tag = self.tag_model(**options) tag.full_clean() diff --git a/tests/testapp/admin.py b/tests/testapp/admin.py index af9a60f2d..e565f6486 100644 --- a/tests/testapp/admin.py +++ b/tests/testapp/admin.py @@ -15,44 +15,44 @@ class BaseAdmin(MultitenantAdminMixin, admin.ModelAdmin): class ShelfAdmin(BaseAdmin): - list_display = ['name', 'organization'] + list_display = ["name", "organization"] list_filter = [MultitenantOrgFilter] - fields = ['name', 'organization', 'tags', 'created', 'modified'] - search_fields = ['name'] - multitenant_shared_relations = ['tags'] + fields = ["name", "organization", "tags", "created", "modified"] + search_fields = ["name"] + multitenant_shared_relations = ["tags"] class ShelfFilter(MultitenantRelatedOrgFilter): - field_name = 'shelf' - parameter_name = 'shelf' - title = _('Shelf') + field_name = "shelf" + parameter_name = "shelf" + title = _("Shelf") class BookAdmin(BaseAdmin): - list_display = ['name', 'author', 'organization', 'shelf'] + list_display = ["name", "author", "organization", "shelf"] list_filter = [ MultitenantOrgFilter, ShelfFilter, ] - fields = ['name', 'author', 'organization', 'shelf', 'created', 'modified'] - multitenant_shared_relations = ['shelf'] + fields = ["name", "author", "organization", "shelf", "created", "modified"] + multitenant_shared_relations = ["shelf"] - def change_view(self, request, object_id, form_url='', extra_context=None): + def change_view(self, request, object_id, form_url="", extra_context=None): extra_context = extra_context or {} extra_context.update( { - 'additional_buttons': [ + "additional_buttons": [ { - 'type': 'button', - 'url': 'DUMMY', - 'class': 'previewbook', - 'value': 'Preview book', + "type": "button", + "url": "DUMMY", + "class": "previewbook", + "value": "Preview book", }, { - 'type': 'button', - 'url': 'DUMMY', - 'class': 'downloadbook', - 'value': 'Download book', + "type": "button", + "url": "DUMMY", + "class": "downloadbook", + "value": "Download book", }, ] } diff --git a/tests/testapp/apps.py b/tests/testapp/apps.py index 246b604b7..e816cf53e 100644 --- a/tests/testapp/apps.py +++ b/tests/testapp/apps.py @@ -2,6 +2,6 @@ class TestAppConfig(AppConfig): - name = 'testapp' - label = 'testapp' - default_auto_field = 'django.db.models.AutoField' + name = "testapp" + label = "testapp" + default_auto_field = "django.db.models.AutoField" diff --git a/tests/testapp/migrations/0001_initial.py b/tests/testapp/migrations/0001_initial.py index 91d3406a4..29f9e173a 100644 --- a/tests/testapp/migrations/0001_initial.py +++ b/tests/testapp/migrations/0001_initial.py @@ -9,57 +9,57 @@ class Migration(migrations.Migration): initial = True - dependencies = [swapper.dependency('openwisp_users', 'Organization')] + dependencies = [swapper.dependency("openwisp_users", "Organization")] operations = [ migrations.CreateModel( - name='Config', + name="Config", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('name', models.CharField(max_length=16)), + ("name", models.CharField(max_length=16)), ( - 'organization', + "organization", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to=swapper.get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=swapper.get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ], - options={'abstract': False}, + options={"abstract": False}, ), migrations.CreateModel( - name='Template', + name="Template", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('name', models.CharField(max_length=16)), + ("name", models.CharField(max_length=16)), ( - 'organization', + "organization", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to=swapper.get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=swapper.get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ], - options={'abstract': False}, + options={"abstract": False}, ), ] diff --git a/tests/testapp/migrations/0002_config_template.py b/tests/testapp/migrations/0002_config_template.py index a633d1d84..b11183348 100644 --- a/tests/testapp/migrations/0002_config_template.py +++ b/tests/testapp/migrations/0002_config_template.py @@ -6,17 +6,17 @@ class Migration(migrations.Migration): - dependencies = [('testapp', '0001_initial')] + dependencies = [("testapp", "0001_initial")] operations = [ migrations.AddField( - model_name='config', - name='template', + model_name="config", + name="template", field=models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to='testapp.Template', + to="testapp.Template", ), ) ] diff --git a/tests/testapp/migrations/0003_multitenancy.py b/tests/testapp/migrations/0003_multitenancy.py index a060e8979..7a9ce58cc 100644 --- a/tests/testapp/migrations/0003_multitenancy.py +++ b/tests/testapp/migrations/0003_multitenancy.py @@ -13,16 +13,16 @@ class Migration(migrations.Migration): dependencies = [ - swapper.dependency('openwisp_users', 'Group'), - ('testapp', '0002_config_template'), + swapper.dependency("openwisp_users", "Group"), + ("testapp", "0002_config_template"), ] operations = [ migrations.CreateModel( - name='Book', + name="Book", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -31,40 +31,40 @@ class Migration(migrations.Migration): ), ), ( - 'created', + "created", model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, - verbose_name='created', + verbose_name="created", ), ), ( - 'modified', + "modified", model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, - verbose_name='modified', + verbose_name="modified", ), ), - ('name', models.CharField(max_length=64, verbose_name='name')), - ('author', models.CharField(max_length=64, verbose_name='author')), + ("name", models.CharField(max_length=64, verbose_name="name")), + ("author", models.CharField(max_length=64, verbose_name="author")), ( - 'organization', + "organization", models.ForeignKey( on_delete=django.db.models.deletion.CASCADE, - to=swapper.get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=swapper.get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ], - options={'abstract': False}, + options={"abstract": False}, bases=(openwisp_users.mixins.ValidateOrgMixin, models.Model), ), migrations.CreateModel( - name='Shelf', + name="Shelf", fields=[ ( - 'id', + "id", models.UUIDField( default=uuid.uuid4, editable=False, @@ -73,75 +73,75 @@ class Migration(migrations.Migration): ), ), ( - 'created', + "created", model_utils.fields.AutoCreatedField( default=django.utils.timezone.now, editable=False, - verbose_name='created', + verbose_name="created", ), ), ( - 'modified', + "modified", model_utils.fields.AutoLastModifiedField( default=django.utils.timezone.now, editable=False, - verbose_name='modified', + verbose_name="modified", ), ), - ('name', models.CharField(max_length=64, verbose_name='name')), + ("name", models.CharField(max_length=64, verbose_name="name")), ( - 'organization', + "organization", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to=swapper.get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=swapper.get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ], - options={'abstract': False}, + options={"abstract": False}, bases=(openwisp_users.mixins.ValidateOrgMixin, models.Model), ), migrations.AddField( - model_name='book', - name='shelf', + model_name="book", + name="shelf", field=models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to='testapp.Shelf' + on_delete=django.db.models.deletion.CASCADE, to="testapp.Shelf" ), ), migrations.CreateModel( - name='Tag', + name="Tag", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('name', models.CharField(max_length=50)), + ("name", models.CharField(max_length=50)), ( - 'organization', + "organization", models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to=swapper.get_model_name('openwisp_users', 'Organization'), - verbose_name='organization', + to=swapper.get_model_name("openwisp_users", "Organization"), + verbose_name="organization", ), ), ], options={ - 'abstract': False, + "abstract": False, }, bases=(openwisp_users.mixins.ValidateOrgMixin, models.Model), ), migrations.AddField( - model_name='shelf', - name='tags', - field=models.ManyToManyField(blank=True, to='testapp.Tag'), + model_name="shelf", + name="tags", + field=models.ManyToManyField(blank=True, to="testapp.Tag"), ), ] diff --git a/tests/testapp/migrations/0004_library.py b/tests/testapp/migrations/0004_library.py index 839fd0dbf..4c1d236cb 100644 --- a/tests/testapp/migrations/0004_library.py +++ b/tests/testapp/migrations/0004_library.py @@ -6,28 +6,28 @@ class Migration(migrations.Migration): dependencies = [ - ('testapp', '0003_multitenancy'), + ("testapp", "0003_multitenancy"), ] operations = [ migrations.CreateModel( - name='Library', + name="Library", fields=[ ( - 'id', + "id", models.AutoField( auto_created=True, primary_key=True, serialize=False, - verbose_name='ID', + verbose_name="ID", ), ), - ('name', models.CharField(max_length=64, verbose_name='name')), - ('address', models.TextField(blank=True, null=True)), + ("name", models.CharField(max_length=64, verbose_name="name")), + ("address", models.TextField(blank=True, null=True)), ( - 'book', + "book", models.ForeignKey( - on_delete=django.db.models.deletion.CASCADE, to='testapp.book' + on_delete=django.db.models.deletion.CASCADE, to="testapp.book" ), ), ], diff --git a/tests/testapp/migrations/0005_default_groups.py b/tests/testapp/migrations/0005_default_groups.py index 84d3458bb..cdd18fdc2 100644 --- a/tests/testapp/migrations/0005_default_groups.py +++ b/tests/testapp/migrations/0005_default_groups.py @@ -6,7 +6,7 @@ class Migration(migrations.Migration): dependencies = [ - ('testapp', '0004_library'), + ("testapp", "0004_library"), ] operations = [ diff --git a/tests/testapp/migrations/0006_alter_book_shelf.py b/tests/testapp/migrations/0006_alter_book_shelf.py index 9c45423d5..b46173edc 100644 --- a/tests/testapp/migrations/0006_alter_book_shelf.py +++ b/tests/testapp/migrations/0006_alter_book_shelf.py @@ -6,18 +6,18 @@ class Migration(migrations.Migration): dependencies = [ - ('testapp', '0005_default_groups'), + ("testapp", "0005_default_groups"), ] operations = [ migrations.AlterField( - model_name='book', - name='shelf', + model_name="book", + name="shelf", field=models.ForeignKey( blank=True, null=True, on_delete=django.db.models.deletion.CASCADE, - to='testapp.shelf', + to="testapp.shelf", ), ), ] diff --git a/tests/testapp/migrations/__init__.py b/tests/testapp/migrations/__init__.py index b4476b84f..b9f4817be 100644 --- a/tests/testapp/migrations/__init__.py +++ b/tests/testapp/migrations/__init__.py @@ -6,9 +6,9 @@ def update_administrator_permissions(apps, schema_editor): - model_name = swapper.get_model_name('openwisp_users', 'Group') + model_name = swapper.get_model_name("openwisp_users", "Group") model_label = swapper.split(model_name)[0] - Group = apps.get_model(model_label, 'Group') + Group = apps.get_model(model_label, "Group") for app_config in apps.get_app_configs(): app_config.models_module = True @@ -16,13 +16,13 @@ def update_administrator_permissions(apps, schema_editor): app_config.models_module = None try: - operator = Group.objects.get(name='Administrator') + operator = Group.objects.get(name="Administrator") permissions = Permission.objects.filter( - Q(codename__endswith='template') - | Q(codename__endswith='shelf') - | Q(codename__endswith='book') - | Q(codename__endswith='tag') - ).values_list('pk', flat=True) + Q(codename__endswith="template") + | Q(codename__endswith="shelf") + | Q(codename__endswith="book") + | Q(codename__endswith="tag") + ).values_list("pk", flat=True) operator.permissions.add(*permissions) except ObjectDoesNotExist: pass diff --git a/tests/testapp/models.py b/tests/testapp/models.py index 4ac7f2573..b43c834fe 100644 --- a/tests/testapp/models.py +++ b/tests/testapp/models.py @@ -13,7 +13,7 @@ def __str__(self): return self.name def clean(self): - self._validate_org_reverse_relation('config_set') + self._validate_org_reverse_relation("config_set") class Config(OrgMixin): @@ -23,7 +23,7 @@ class Config(OrgMixin): ) def clean(self): - self._validate_org_relation('template') + self._validate_org_relation("template") class Tag(ShareableOrgMixin): @@ -34,7 +34,7 @@ def __str__(self): class Shelf(ShareableOrgMixin, TimeStampedEditableModel): - name = models.CharField(_('name'), max_length=64) + name = models.CharField(_("name"), max_length=64) tags = models.ManyToManyField(Tag, blank=True) def __str__(self): @@ -45,15 +45,15 @@ class Meta: def clean(self): if self.name == "Intentional_Test_Fail": - raise ValidationError('Intentional_Test_Fail') + raise ValidationError("Intentional_Test_Fail") return self class Book(OrgMixin, TimeStampedEditableModel): - name = models.CharField(_('name'), max_length=64) - author = models.CharField(_('author'), max_length=64) + name = models.CharField(_("name"), max_length=64) + author = models.CharField(_("author"), max_length=64) shelf = models.ForeignKey( - 'testapp.Shelf', on_delete=models.CASCADE, blank=True, null=True + "testapp.Shelf", on_delete=models.CASCADE, blank=True, null=True ) def __str__(self): @@ -64,9 +64,9 @@ class Meta: class Library(models.Model): - name = models.CharField(_('name'), max_length=64) + name = models.CharField(_("name"), max_length=64) address = models.TextField(null=True, blank=True) - book = models.ForeignKey('testapp.Book', on_delete=models.CASCADE) + book = models.ForeignKey("testapp.Book", on_delete=models.CASCADE) def __str__(self): return self.name diff --git a/tests/testapp/serializers.py b/tests/testapp/serializers.py index 56b3a0084..66008f278 100644 --- a/tests/testapp/serializers.py +++ b/tests/testapp/serializers.py @@ -11,8 +11,8 @@ class BookMemberSerializer(FilterSerializerByOrgMembership, ValidatedModelSerializer): class Meta: model = Book - fields = '__all__' - read_only_fields = ('created', 'modified') + fields = "__all__" + read_only_fields = ("created", "modified") class BookManagerSerializer(FilterSerializerByOrgManaged, ValidatedModelSerializer): @@ -20,48 +20,48 @@ class BookManagerSerializer(FilterSerializerByOrgManaged, ValidatedModelSerializ class Meta: model = Book - fields = '__all__' - read_only_fields = ('created', 'modified') + fields = "__all__" + read_only_fields = ("created", "modified") class BookOwnerSerializer(FilterSerializerByOrgOwned, ValidatedModelSerializer): class Meta: model = Book - fields = '__all__' - read_only_fields = ('created', 'modified') + fields = "__all__" + read_only_fields = ("created", "modified") class BookSerializer(ValidatedModelSerializer): class Meta: model = Book - fields = '__all__' - read_only_fields = ('created', 'modified') + fields = "__all__" + read_only_fields = ("created", "modified") class ShelfSerializer(ValidatedModelSerializer): class Meta: model = Shelf - fields = '__all__' - read_only_fields = ('created', 'modified') + fields = "__all__" + read_only_fields = ("created", "modified") class TemplateSerializer(FilterSerializerByOrgManaged, ValidatedModelSerializer): class Meta: model = Template - fields = '__all__' + fields = "__all__" class LibrarySerializer(FilterSerializerByOrgManaged, ValidatedModelSerializer): class Meta: model = Library - fields = '__all__' + fields = "__all__" class ShelfSerializerForBook(FilterSerializerByOrgManaged, ValidatedModelSerializer): class Meta: model = Shelf - fields = '__all__' - read_only_fields = ('created', 'modified') + fields = "__all__" + read_only_fields = ("created", "modified") class BookWithNestedShelfSerializer( @@ -71,14 +71,14 @@ class BookWithNestedShelfSerializer( class Meta: model = Book - fields = '__all__' - read_only_fields = ('created', 'modified') + fields = "__all__" + read_only_fields = ("created", "modified") def validate(self, data): - if data.get('shelf'): - shelf_data = data.pop('shelf') + if data.get("shelf"): + shelf_data = data.pop("shelf") shelf_obj = Shelf.objects.create(**shelf_data) - data.update({'shelf': shelf_obj}) + data.update({"shelf": shelf_obj}) instance = self.instance or self.Meta.model(**data) instance.full_clean() return data @@ -89,5 +89,5 @@ class ShelfWithReadOnlyOrgSerializer( ): class Meta: model = Shelf - fields = '__all__' - read_only_fields = ('organization', 'created', 'modified') + fields = "__all__" + read_only_fields = ("organization", "created", "modified") diff --git a/tests/testapp/tests/test_admin.py b/tests/testapp/tests/test_admin.py index f451d772d..b27b3a337 100644 --- a/tests/testapp/tests/test_admin.py +++ b/tests/testapp/tests/test_admin.py @@ -10,16 +10,16 @@ from ..models import Template -Organization = load_model('openwisp_users', 'Organization') -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') -OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner') +Organization = load_model("openwisp_users", "Organization") +OrganizationUser = load_model("openwisp_users", "OrganizationUser") +OrganizationOwner = load_model("openwisp_users", "OrganizationOwner") User = get_user_model() -Group = load_model('openwisp_users', 'Group') +Group = load_model("openwisp_users", "Group") class TestUsersAdmin(TestOrganizationMixin, TestCase): app_label = ( - 'openwisp_users' if not os.environ.get('SAMPLE_APP', False) else 'sample_users' + "openwisp_users" if not os.environ.get("SAMPLE_APP", False) else "sample_users" ) def test_group_reversion(self): @@ -27,18 +27,18 @@ def test_group_reversion(self): self.client.force_login(admin) test_group = Group.objects.create() self.client.post( - reverse(f'admin:{self.app_label}_group_change', args=(test_group.id,)), - {'name': 'test_group_v1'}, + reverse(f"admin:{self.app_label}_group_change", args=(test_group.id,)), + {"name": "test_group_v1"}, follow=True, ) r = self.client.get( - reverse(f'admin:{self.app_label}_group_revision', args=(test_group.id, 1)) + reverse(f"admin:{self.app_label}_group_revision", args=(test_group.id, 1)) ) self.assertEqual(r.status_code, 200) - self.assertContains(r, '

    Revert test_group_v1

    ') + self.assertContains(r, "

    Revert test_group_v1

    ") def test_accounts_login(self): - r = self.client.get(reverse('account_login'), follow=True) + r = self.client.get(reverse("account_login"), follow=True) self.assertEqual(r.status_code, 200) self.assertContains( r, '', html=True @@ -50,10 +50,10 @@ def test_org_admin_create_shareable_template(self): administrator = self._create_administrator() self.client.force_login(administrator) response = self.client.post( - reverse('admin:testapp_template_add'), + reverse("admin:testapp_template_add"), data={ - 'name': 'test-template', - 'organization': '', + "name": "test-template", + "organization": "", }, follow=True, ) @@ -62,8 +62,8 @@ def test_org_admin_create_shareable_template(self): ( '
    \n' '
      ' - '
    • This field is required.
    ' - ).format(' id="id_organization_error"' if django.VERSION >= (5, 2) else ''), + "
  • This field is required.
  • " + ).format(' id="id_organization_error"' if django.VERSION >= (5, 2) else ""), ) self.assertEqual(Template.objects.count(), 0) @@ -71,10 +71,10 @@ def test_superuser_create_shareable_template(self): admin = self._create_admin() self.client.force_login(admin) response = self.client.post( - reverse('admin:testapp_template_add'), + reverse("admin:testapp_template_add"), data={ - 'name': 'test-template', - 'organization': '', + "name": "test-template", + "organization": "", }, follow=True, ) diff --git a/tests/testapp/tests/test_filter_classes.py b/tests/testapp/tests/test_filter_classes.py index 38f35af23..71d1dcb04 100644 --- a/tests/testapp/tests/test_filter_classes.py +++ b/tests/testapp/tests/test_filter_classes.py @@ -11,8 +11,8 @@ from ..models import Book, Library, Shelf, Tag from .mixins import TestMultitenancyMixin -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') -OrganizationOwner = load_model('openwisp_users', 'OrganizationOwner') +OrganizationUser = load_model("openwisp_users", "OrganizationUser") +OrganizationOwner = load_model("openwisp_users", "OrganizationOwner") User = get_user_model() @@ -23,34 +23,34 @@ def setUp(self): self.book_model = Book self.library_model = Library self.tag_model = Tag - self._create_org(name='org_a', slug='org_a') - self._create_org(name='org_b', slug='org_b') + self._create_org(name="org_a", slug="org_a") + self._create_org(name="org_b", slug="org_b") self.tag_a = self._create_tag( - name='test-tag-a', organization=self._get_org('org_a') + name="test-tag-a", organization=self._get_org("org_a") ) self.tag_b = self._create_tag( - name='test-tag-b', organization=self._get_org('org_b') + name="test-tag-b", organization=self._get_org("org_b") ) self.shelf_a = self._create_shelf( - name='test-shelf-a', organization=self._get_org('org_a') + name="test-shelf-a", organization=self._get_org("org_a") ) self.shelf_b = self._create_shelf( - name='test-shelf-b', organization=self._get_org('org_b') + name="test-shelf-b", organization=self._get_org("org_b") ) self.shelf_a.tags.add(self.tag_a) self.shelf_b.tags.add(self.tag_b) self.book1 = self._create_book( - name='book1', organization=self._get_org('org_a'), shelf=self.shelf_a + name="book1", organization=self._get_org("org_a"), shelf=self.shelf_a ) self.book2 = self._create_book( - name='book2', organization=self._get_org('org_a'), shelf=self.shelf_b + name="book2", organization=self._get_org("org_a"), shelf=self.shelf_b ) def _assert_django_filters_shelf_options(self, response, shelf_a, shelf_b): - self.assertEqual(response.data[0]['id'], str(shelf_a.id)) + self.assertEqual(response.data[0]["id"], str(shelf_a.id)) # make sure only correct organization is # visible in the django filters select options - self.assertContains(response, 'org_a') + self.assertContains(response, "org_a") # As Shelf API Views use reusable OrganizationFilter classes, # the response should include both organization # and organization slug filter options @@ -64,49 +64,49 @@ def _assert_django_filters_shelf_options(self, response, shelf_a, shelf_b): """, html=True, ) - self.assertContains(response, 'test-tag-a') + self.assertContains(response, "test-tag-a") self.assertNotContains(response, str(shelf_b.id)) - self.assertNotContains(response, 'org_b') - self.assertNotContains(response, 'default') - self.assertNotContains(response, 'test-shelf-a') - self.assertNotContains(response, 'test-shelf-b') - self.assertNotContains(response, 'test-tag-b') + self.assertNotContains(response, "org_b") + self.assertNotContains(response, "default") + self.assertNotContains(response, "test-shelf-a") + self.assertNotContains(response, "test-shelf-b") + self.assertNotContains(response, "test-tag-b") def test_browsable_api_filter_manager(self): operator = self._get_operator() # First user is automatically owner, so created dummy # user to keep operator as manager only. self._create_org_user( - user=self._get_user(), is_admin=True, organization=self._get_org('org_a') + user=self._get_user(), is_admin=True, organization=self._get_org("org_a") ) self._create_org_user( - user=operator, is_admin=True, organization=self._get_org('org_a') + user=operator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token(operator) url = f'{reverse("test_books_list_manager_view", args=(self.shelf_a.id,))}' response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) - self.assertContains(response, 'org_a') - self.assertContains(response, 'test-shelf-a') - self.assertNotContains(response, 'org_b') - self.assertNotContains(response, 'test-shelf-b') + self.assertContains(response, "org_a") + self.assertContains(response, "test-shelf-a") + self.assertNotContains(response, "org_b") + self.assertNotContains(response, "test-shelf-b") def test_browsable_api_filter_owner(self): operator = self._get_operator() # First user is automatically owner self._create_org_user( - user=operator, is_admin=True, organization=self._get_org('org_a') + user=operator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token() url = f'{reverse("test_books_list_manager_view", args=(self.shelf_a.id,))}' response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) - self.assertContains(response, 'org_a') - self.assertContains(response, 'test-shelf-a') - self.assertNotContains(response, 'org_b') - self.assertNotContains(response, 'test-shelf-b') + self.assertContains(response, "org_a") + self.assertContains(response, "test-shelf-a") + self.assertNotContains(response, "org_b") + self.assertNotContains(response, "test-shelf-b") def test_browsable_api_filter_superuser(self): admin = self._get_admin() @@ -114,305 +114,305 @@ def test_browsable_api_filter_superuser(self): self.client.force_login(admin) url = f'{reverse("test_books_list_manager_view", args=(self.shelf_a.id,))}' response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) - self.assertContains(response, 'org_a') - self.assertContains(response, 'test-shelf-a') - self.assertContains(response, 'org_b') - self.assertContains(response, 'test-shelf-b') + self.assertContains(response, "org_a") + self.assertContains(response, "test-shelf-a") + self.assertContains(response, "org_b") + self.assertContains(response, "test-shelf-b") def test_filter_by_parent_membership(self): operator = self._get_operator() - self._create_org_user(user=operator, organization=self._get_org('org_a')) + self._create_org_user(user=operator, organization=self._get_org("org_a")) token = self._obtain_auth_token(operator) - url = reverse('test_books_list_member_view', args=(self.shelf_a.id,)) - response = self.client.get(url, HTTP_AUTHORIZATION=f'Bearer {token}') - self.assertEqual(response.data[0]['id'], str(self.book1.id)) + url = reverse("test_books_list_member_view", args=(self.shelf_a.id,)) + response = self.client.get(url, HTTP_AUTHORIZATION=f"Bearer {token}") + self.assertEqual(response.data[0]["id"], str(self.book1.id)) self.assertNotContains(response, str(self.book2.id)) def test_filter_by_parent_managed(self): operator = self._get_operator() self._create_org_user( - user=self._get_user(), is_admin=True, organization=self._get_org('org_a') + user=self._get_user(), is_admin=True, organization=self._get_org("org_a") ) self._create_org_user( - user=operator, is_admin=True, organization=self._get_org('org_a') + user=operator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token(operator) - url = reverse('test_books_list_manager_view', args=(self.shelf_a.id,)) - response = self.client.get(url, HTTP_AUTHORIZATION=f'Bearer {token}') - self.assertEqual(response.data[0]['id'], str(self.book1.id)) + url = reverse("test_books_list_manager_view", args=(self.shelf_a.id,)) + response = self.client.get(url, HTTP_AUTHORIZATION=f"Bearer {token}") + self.assertEqual(response.data[0]["id"], str(self.book1.id)) self.assertNotContains(response, str(self.book2.id)) def test_filter_by_parent_owned(self): operator = self._get_operator() self._create_org_user( - user=operator, is_admin=True, organization=self._get_org('org_a') + user=operator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token() - url = reverse('test_books_list_owner_view', args=(self.shelf_a.id,)) - response = self.client.get(url, HTTP_AUTHORIZATION=f'Bearer {token}') - self.assertEqual(response.data[0]['id'], str(self.book1.id)) + url = reverse("test_books_list_owner_view", args=(self.shelf_a.id,)) + response = self.client.get(url, HTTP_AUTHORIZATION=f"Bearer {token}") + self.assertEqual(response.data[0]["id"], str(self.book1.id)) self.assertNotContains(response, str(self.book2.id)) def test_filter_by_org_membership(self): operator = self._get_operator() - self._create_org_user(user=operator, organization=self._get_org('org_a')) + self._create_org_user(user=operator, organization=self._get_org("org_a")) token = self._obtain_auth_token(operator) - url = reverse('test_shelf_list_member_view') - response = self.client.get(url, HTTP_AUTHORIZATION=f'Bearer {token}') - self.assertEqual(response.data[0]['id'], str(self.shelf_a.id)) + url = reverse("test_shelf_list_member_view") + response = self.client.get(url, HTTP_AUTHORIZATION=f"Bearer {token}") + self.assertEqual(response.data[0]["id"], str(self.shelf_a.id)) self.assertNotContains(response, str(self.shelf_b.id)) def test_filter_by_org_managed(self): operator = self._get_operator() self._create_org_user( - user=self._get_user(), is_admin=True, organization=self._get_org('org_a') + user=self._get_user(), is_admin=True, organization=self._get_org("org_a") ) self._create_org_user( - user=operator, is_admin=True, organization=self._get_org('org_a') + user=operator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token(operator) - url = reverse('test_shelf_list_manager_view') - response = self.client.get(url, HTTP_AUTHORIZATION=f'Bearer {token}') - self.assertEqual(response.data[0]['id'], str(self.shelf_a.id)) + url = reverse("test_shelf_list_manager_view") + response = self.client.get(url, HTTP_AUTHORIZATION=f"Bearer {token}") + self.assertEqual(response.data[0]["id"], str(self.shelf_a.id)) self.assertNotContains(response, str(self.shelf_b.id)) def test_filter_by_org_managed_shared_objects(self): - self._create_shelf(name='shared_shelf', organization=None) + self._create_shelf(name="shared_shelf", organization=None) operator = self._get_operator() self._create_org_user( - user=operator, is_admin=True, organization=self._get_org('org_a') + user=operator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token(operator) url = f'{reverse("test_books_list_manager_view", args=(self.shelf_a.id,))}' response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) - self.assertContains(response, 'shared_shelf') + self.assertContains(response, "shared_shelf") def test_filter_by_org_owned(self): operator = self._get_operator() self._create_org_user( - user=operator, is_admin=True, organization=self._get_org('org_a') + user=operator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token() - url = reverse('test_shelf_list_owner_view') + url = reverse("test_shelf_list_owner_view") response = self.client.get( url, - HTTP_AUTHORIZATION=f'Bearer {token}', + HTTP_AUTHORIZATION=f"Bearer {token}", ) - self.assertEqual(response.data[0]['id'], str(self.shelf_a.id)) + self.assertEqual(response.data[0]["id"], str(self.shelf_a.id)) self.assertNotContains(response, str(self.shelf_b.id)) def test_browsable_api_filter_member(self): operator = self._get_operator() - self._create_org_user(user=operator, organization=self._get_org('org_a')) + self._create_org_user(user=operator, organization=self._get_org("org_a")) token = self._obtain_auth_token(operator) url = f'{reverse("test_books_list_member_view", args=(self.shelf_a.id,))}' response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) - self.assertContains(response, 'org_a') - self.assertContains(response, 'test-shelf-a') - self.assertNotContains(response, 'org_b') - self.assertNotContains(response, 'test-shelf-b') + self.assertContains(response, "org_a") + self.assertContains(response, "test-shelf-a") + self.assertNotContains(response, "org_b") + self.assertNotContains(response, "test-shelf-b") def test_unauthorized_user(self): r = self.client.get(reverse("test_shelf_list_unauthorized_view")) self.assertEqual(r.status_code, 401) r = self.client.get( - reverse('test_book_list_unauthorized_view', args=(self.shelf_a.id,)) + reverse("test_book_list_unauthorized_view", args=(self.shelf_a.id,)) ) self.assertEqual(r.status_code, 401) def test_presence_of_null_org_field(self): administrator = self._create_administrator() self._create_org_user( - user=administrator, is_admin=True, organization=self._get_org('org_a') + user=administrator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token(administrator) - url = reverse('test_template_list') + url = reverse("test_template_list") response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) - self.assertNotContains(response, '--------') + self.assertNotContains(response, "--------") def test_filter_by_org_managed_with_org_field(self): - shared_shelf = self._create_shelf(name='shared_shelf', organization=None) - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') + shared_shelf = self._create_shelf(name="shared_shelf", organization=None) + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") book_org1 = self._create_book( - name='book_org1', organization=org1, shelf=shared_shelf + name="book_org1", organization=org1, shelf=shared_shelf ) book_org2 = self._create_book( - name='book_org2', organization=org2, shelf=shared_shelf + name="book_org2", organization=org2, shelf=shared_shelf ) - lib1 = self._create_library(name='lib1', book=book_org1) - lib2 = self._create_library(name='lib2', book=book_org2) + lib1 = self._create_library(name="lib1", book=book_org1) + lib2 = self._create_library(name="lib2", book=book_org2) operator = self._get_operator() self._create_org_user(user=operator, is_admin=True, organization=org1) token = self._obtain_auth_token(operator) - url = reverse('test_library_list') - response = self.client.get(url, HTTP_AUTHORIZATION=f'Bearer {token}') + url = reverse("test_library_list") + response = self.client.get(url, HTTP_AUTHORIZATION=f"Bearer {token}") self.assertEqual(response.status_code, 200) self.assertEqual(len(response.data), 1) url = f'{reverse("test_library_detail", args=(lib1.id,))}' response = self.client.get( - url, args=(lib1.id), HTTP_AUTHORIZATION=f'Bearer {token}' + url, args=(lib1.id), HTTP_AUTHORIZATION=f"Bearer {token}" ) self.assertEqual(response.status_code, 200) url = f'{reverse("test_library_detail", args=(lib2.id,))}' response = self.client.get( - url, args=(lib1.id), HTTP_AUTHORIZATION=f'Bearer {token}' + url, args=(lib1.id), HTTP_AUTHORIZATION=f"Bearer {token}" ) self.assertEqual(response.status_code, 404) def test_get_book_nested_shelf(self): administrator = self._create_administrator() self._create_org_user( - user=administrator, is_admin=True, organization=self._get_org('org_a') + user=administrator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token(administrator) - url = reverse('test_book_nested_shelf') + url = reverse("test_book_nested_shelf") with self.assertNumQueries(8): - response = self.client.get(url, HTTP_AUTHORIZATION=f'Bearer {token}') + response = self.client.get(url, HTTP_AUTHORIZATION=f"Bearer {token}") self.assertEqual(response.status_code, 200) def test_post_book_nested_shelf(self): - org1 = self._get_org('org_a') + org1 = self._get_org("org_a") administrator = self._create_administrator() self._create_org_user( - user=administrator, is_admin=True, organization=self._get_org('org_a') + user=administrator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token(administrator) - url = reverse('test_book_nested_shelf') + url = reverse("test_book_nested_shelf") data = { - 'shelf': {'name': 'test-shelf', 'organization': org1.pk}, - 'name': 'test-book', - 'author': 'test-auther', - 'organization': org1.pk, + "shelf": {"name": "test-shelf", "organization": org1.pk}, + "name": "test-book", + "author": "test-auther", + "organization": org1.pk, } with self.assertNumQueries(13): response = self.client.post( url, data, - content_type='application/json', - HTTP_AUTHORIZATION=f'Bearer {token}', + content_type="application/json", + HTTP_AUTHORIZATION=f"Bearer {token}", ) self.assertEqual(response.status_code, 201) self.assertEqual(Shelf.objects.count(), 3) self.assertEqual(Book.objects.count(), 3) def test_shelf_with_read_only_org_field(self): - org1 = self._create_org(name='org1') + org1 = self._create_org(name="org1") operator = self._get_operator() self._create_org_user(user=operator, is_admin=True, organization=org1) self.client.force_login(operator) - self._create_shelf(name='test-shelf-a', organization=org1) - path = reverse('test_shelf_list_with_read_only_org') + self._create_shelf(name="test-shelf-a", organization=org1) + path = reverse("test_shelf_list_with_read_only_org") expected_queries = ( - 6 if version_parse(REST_FRAMEWORK_VERSION) > version_parse('3.14') else 7 + 6 if version_parse(REST_FRAMEWORK_VERSION) > version_parse("3.14") else 7 ) with self.assertNumQueries(expected_queries): - response = self.client.get(path, {'format': 'api'}) + response = self.client.get(path, {"format": "api"}) self.assertEqual(response.status_code, 200) - self.assertEqual(response.data[0]['organization'], org1.pk) - self.assertNotContains(response, 'org1') + self.assertEqual(response.data[0]["organization"], org1.pk) + self.assertNotContains(response, "org1") def test_django_filters_superuser(self): admin = self._get_admin() token = self._obtain_auth_token(admin) - url = reverse('test_shelf_list_member_view') + url = reverse("test_shelf_list_member_view") response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) - self.assertEqual(response.data[0]['id'], str(self.shelf_a.id)) + self.assertEqual(response.data[0]["id"], str(self.shelf_a.id)) # superuser can see every filter options self.assertContains(response, str(self.shelf_b.id)) - self.assertContains(response, 'org_a') - self.assertContains(response, 'org_b') - self.assertContains(response, 'default') + self.assertContains(response, "org_a") + self.assertContains(response, "org_b") + self.assertContains(response, "default") self.assertContains(response, 'id="id_tags" multiple>') def test_django_filters_by_field_other_than_organization(self): - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') - book_org1 = self._create_book(name='book_o1', organization=org1) - book_org2 = self._create_book(name='book_o2', organization=org2) - lib1 = self._create_library(name='lib1', book=book_org1) - self._create_library(name='lib2', book=book_org2) + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") + book_org1 = self._create_book(name="book_o1", organization=org1) + book_org2 = self._create_book(name="book_o2", organization=org2) + lib1 = self._create_library(name="lib1", book=book_org1) + self._create_library(name="lib2", book=book_org2) operator = self._get_operator() self._create_org_user(user=operator, is_admin=True, organization=org1) token = self._obtain_auth_token(operator) - url = reverse('test_library_list') + url = reverse("test_library_list") response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) - self.assertEqual(response.data[0]['id'], lib1.id) + self.assertEqual(response.data[0]["id"], lib1.id) self.assertEqual(len(response.data), 1) # ensure that only the 'books' belonging to 'org1' # and 'org1' are visible in the django-filters select options - self.assertContains(response, 'book_o1') - self.assertContains(response, 'org1') - self.assertNotContains(response, 'book_o2') - self.assertNotContains(response, 'org2') - self.assertNotContains(response, 'default') - self.assertNotContains(response, 'lib1') - self.assertNotContains(response, 'lib2') + self.assertContains(response, "book_o1") + self.assertContains(response, "org1") + self.assertNotContains(response, "book_o2") + self.assertNotContains(response, "org2") + self.assertNotContains(response, "default") + self.assertNotContains(response, "lib1") + self.assertNotContains(response, "lib2") def test_django_filters_related_organization_field(self): - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') - book_org1 = self._create_book(name='book_o1', organization=org1) - book_org2 = self._create_book(name='book_o2', organization=org2) - lib1 = self._create_library(name='lib1', book=book_org1) - self._create_library(name='lib2', book=book_org2) + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") + book_org1 = self._create_book(name="book_o1", organization=org1) + book_org2 = self._create_book(name="book_o2", organization=org2) + lib1 = self._create_library(name="lib1", book=book_org1) + self._create_library(name="lib2", book=book_org2) operator = self._get_operator() self._create_org_user(user=operator, is_admin=True, organization=org1) token = self._obtain_auth_token(operator) # The 'LibraryListFilter' field includes # related organization field ('book__organization') - url = reverse('test_library_list') + url = reverse("test_library_list") response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) - self.assertEqual(response.data[0]['id'], lib1.id) + self.assertEqual(response.data[0]["id"], lib1.id) self.assertEqual(len(response.data), 1) # ensure that only the 'books' belonging to 'org1' # and 'org1' are visible in the django-filters select options - self.assertContains(response, 'book_o1') - self.assertContains(response, 'org1') - self.assertNotContains(response, 'book_o2') - self.assertNotContains(response, 'org2') - self.assertNotContains(response, 'default') - self.assertNotContains(response, 'lib1') - self.assertNotContains(response, 'lib2') + self.assertContains(response, "book_o1") + self.assertContains(response, "org1") + self.assertNotContains(response, "book_o2") + self.assertNotContains(response, "org2") + self.assertNotContains(response, "default") + self.assertNotContains(response, "lib1") + self.assertNotContains(response, "lib2") # Now ensure that filtering with the # related organization field is properly working or not response = self.client.get( - url, {'book__organization': org1.pk}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"book__organization": org1.pk}, HTTP_AUTHORIZATION=f"Bearer {token}" ) - self.assertEqual(response.data[0]['id'], lib1.id) + self.assertEqual(response.data[0]["id"], lib1.id) self.assertEqual(len(response.data), 1) response = self.client.get( - url, {'book__organization': org2.pk}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"book__organization": org2.pk}, HTTP_AUTHORIZATION=f"Bearer {token}" ) - err = 'That choice is not one of the available choices' + err = "That choice is not one of the available choices" self.assertEqual(response.status_code, 400) - self.assertIn(err, str(response.data['book__organization'][0])) + self.assertIn(err, str(response.data["book__organization"][0])) def test_django_filters_by_org_membership(self): operator = self._get_operator() - self._create_org_user(user=operator, organization=self._get_org('org_a')) + self._create_org_user(user=operator, organization=self._get_org("org_a")) token = self._obtain_auth_token(operator) - url = reverse('test_shelf_list_member_view') + url = reverse("test_shelf_list_member_view") response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) self._assert_django_filters_shelf_options(response, self.shelf_a, self.shelf_b) @@ -420,26 +420,26 @@ def test_django_filters_by_org_membership(self): def test_django_filters_by_org_managed(self): operator = self._get_operator() self._create_org_user( - user=self._get_user(), is_admin=True, organization=self._get_org('org_a') + user=self._get_user(), is_admin=True, organization=self._get_org("org_a") ) self._create_org_user( - user=operator, is_admin=True, organization=self._get_org('org_a') + user=operator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token(operator) - url = reverse('test_shelf_list_manager_view') + url = reverse("test_shelf_list_manager_view") response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) self._assert_django_filters_shelf_options(response, self.shelf_a, self.shelf_b) def test_django_filters_by_org_owned(self): operator = self._get_operator() self._create_org_user( - user=operator, is_admin=True, organization=self._get_org('org_a') + user=operator, is_admin=True, organization=self._get_org("org_a") ) token = self._obtain_auth_token() - url = reverse('test_shelf_list_owner_view') + url = reverse("test_shelf_list_owner_view") response = self.client.get( - url, {'format': 'api'}, HTTP_AUTHORIZATION=f'Bearer {token}' + url, {"format": "api"}, HTTP_AUTHORIZATION=f"Bearer {token}" ) self._assert_django_filters_shelf_options(response, self.shelf_a, self.shelf_b) diff --git a/tests/testapp/tests/test_multitenancy.py b/tests/testapp/tests/test_multitenancy.py index 6b29a7d6d..6a704df4c 100644 --- a/tests/testapp/tests/test_multitenancy.py +++ b/tests/testapp/tests/test_multitenancy.py @@ -10,21 +10,21 @@ class TestMultitenancy(TestMultitenancyMixin, TestCase): shelf_model = Shelf def _create_multitenancy_test_env(self): - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') - inactive = self._create_org(name='inactive-org', is_active=False) + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") + inactive = self._create_org(name="inactive-org", is_active=False) operator = self._create_operator( organizations=[org1, inactive], ) administrator = self._create_administrator( organizations=[org1, inactive], ) - s1 = self._create_shelf(name='shell1', organization=org1) - s2 = self._create_shelf(name='shell2', organization=org2) - s3 = self._create_shelf(name='shell3', organization=inactive) - b1 = self._create_book(name='book1', organization=org1, shelf=s1) - b2 = self._create_book(name='book2', organization=org2, shelf=s2) - b3 = self._create_book(name='book3', organization=inactive, shelf=s3) + s1 = self._create_shelf(name="shell1", organization=org1) + s2 = self._create_shelf(name="shell2", organization=org2) + s3 = self._create_shelf(name="shell3", organization=inactive) + b1 = self._create_book(name="book1", organization=org1, shelf=s1) + b2 = self._create_book(name="book2", organization=org2, shelf=s2) + b3 = self._create_book(name="book3", organization=inactive, shelf=s3) data = dict( s1=s1, s2=s2, @@ -43,27 +43,27 @@ def _create_multitenancy_test_env(self): def test_shelf_queryset(self): data = self._create_multitenancy_test_env() self._test_multitenant_admin( - url=reverse('admin:testapp_shelf_changelist'), - visible=[data['s1'].name, data['org1'].name], - hidden=[data['s2'].name, data['org2'].name, data['s3_inactive'].name], + url=reverse("admin:testapp_shelf_changelist"), + visible=[data["s1"].name, data["org1"].name], + hidden=[data["s2"].name, data["org2"].name, data["s3_inactive"].name], administrator=True, ) def test_book_queryset(self): data = self._create_multitenancy_test_env() self._test_multitenant_admin( - url=reverse('admin:testapp_book_changelist'), - visible=[data['b1'].name, data['org1'].name], - hidden=[data['b2'].name, data['org2'].name, data['b3_inactive'].name], + url=reverse("admin:testapp_book_changelist"), + visible=[data["b1"].name, data["org1"].name], + hidden=[data["b2"].name, data["org2"].name, data["b3_inactive"].name], administrator=True, ) def test_book_shelf_fk_queryset(self): data = self._create_multitenancy_test_env() self._test_multitenant_admin( - url=reverse('admin:testapp_book_add'), - visible=[data['s1'].name], - hidden=[data['s2'].name, data['s3_inactive'].name], + url=reverse("admin:testapp_book_add"), + visible=[data["s1"].name], + hidden=[data["s2"].name, data["s3_inactive"].name], select_widget=True, administrator=True, ) diff --git a/tests/testapp/tests/test_permission_classes.py b/tests/testapp/tests/test_permission_classes.py index 72ccc9ee2..11746c4e1 100644 --- a/tests/testapp/tests/test_permission_classes.py +++ b/tests/testapp/tests/test_permission_classes.py @@ -10,29 +10,29 @@ from .mixins import TestMultitenancyMixin User = get_user_model() -Group = load_model('openwisp_users', 'Group') -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') +Group = load_model("openwisp_users", "Group") +OrganizationUser = load_model("openwisp_users", "OrganizationUser") class TestPermissionClasses(TestMultitenancyMixin, TestCase): def setUp(self): AuthRateThrottle.rate = 0 self.template_model = Template - self.member_url = reverse('test_api_member_view') - self.manager_url = reverse('test_api_manager_view') - self.owner_url = reverse('test_api_owner_view') + self.member_url = reverse("test_api_member_view") + self.manager_url = reverse("test_api_manager_view") + self.owner_url = reverse("test_api_owner_view") def test_operator_none(self): self._get_operator() token = self._obtain_auth_token() - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - with self.subTest('Organization Member'): + auth = dict(HTTP_AUTHORIZATION=f"Bearer {token}") + with self.subTest("Organization Member"): response = self.client.get(self.member_url, **auth) self.assertEqual(response.status_code, 403) - with self.subTest('Organization Manager'): + with self.subTest("Organization Manager"): response = self.client.get(self.manager_url, **auth) self.assertEqual(response.status_code, 403) - with self.subTest('Organization Owner'): + with self.subTest("Organization Owner"): response = self.client.get(self.owner_url, **auth) self.assertEqual(response.status_code, 403) @@ -40,14 +40,14 @@ def test_operator_member(self): operator = self._get_operator() self._create_org_user(user=operator) token = self._obtain_auth_token() - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - with self.subTest('Organization Member'): + auth = dict(HTTP_AUTHORIZATION=f"Bearer {token}") + with self.subTest("Organization Member"): response = self.client.get(self.member_url, **auth) self.assertEqual(response.status_code, 200) - with self.subTest('Organization Manager'): + with self.subTest("Organization Manager"): response = self.client.get(self.manager_url, **auth) self.assertEqual(response.status_code, 403) - with self.subTest('Organization Owner'): + with self.subTest("Organization Owner"): response = self.client.get(self.owner_url, **auth) self.assertEqual(response.status_code, 403) @@ -58,14 +58,14 @@ def test_operator_manager(self): self._create_org_user(user=self._get_user(), is_admin=True) self._create_org_user(user=operator, is_admin=True) token = self._obtain_auth_token() - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - with self.subTest('Organization Member'): + auth = dict(HTTP_AUTHORIZATION=f"Bearer {token}") + with self.subTest("Organization Member"): response = self.client.get(self.member_url, **auth) self.assertEqual(response.status_code, 200) - with self.subTest('Organization Manager'): + with self.subTest("Organization Manager"): response = self.client.get(self.manager_url, **auth) self.assertEqual(response.status_code, 200) - with self.subTest('Organization Owner'): + with self.subTest("Organization Owner"): response = self.client.get(self.owner_url, **auth) self.assertEqual(response.status_code, 403) @@ -74,14 +74,14 @@ def test_operator_owner(self): # First user is automatically owner self._create_org_user(user=operator, is_admin=True) token = self._obtain_auth_token() - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - with self.subTest('Organization Member'): + auth = dict(HTTP_AUTHORIZATION=f"Bearer {token}") + with self.subTest("Organization Member"): response = self.client.get(self.member_url, **auth) self.assertEqual(response.status_code, 200) - with self.subTest('Organization Manager'): + with self.subTest("Organization Manager"): response = self.client.get(self.manager_url, **auth) self.assertEqual(response.status_code, 200) - with self.subTest('Organization Owner'): + with self.subTest("Organization Owner"): response = self.client.get(self.owner_url, **auth) self.assertEqual(response.status_code, 200) @@ -89,14 +89,14 @@ def test_superuser(self): admin = self._get_admin() token = self._obtain_auth_token(username=admin) self.client.force_login(admin) - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - with self.subTest('Organization Member'): + auth = dict(HTTP_AUTHORIZATION=f"Bearer {token}") + with self.subTest("Organization Member"): response = self.client.get(self.member_url, **auth) self.assertEqual(response.status_code, 200) - with self.subTest('Organization Manager'): + with self.subTest("Organization Manager"): response = self.client.get(self.manager_url, **auth) self.assertEqual(response.status_code, 200) - with self.subTest('Organization Owner'): + with self.subTest("Organization Owner"): response = self.client.get(self.owner_url, **auth) self.assertEqual(response.status_code, 200) @@ -104,188 +104,188 @@ def test_base_org_perm_fails(self): admin = self._get_operator() token = self._obtain_auth_token(username=admin) self.client.force_login(admin) - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - base_org_permissions_url = reverse('test_base_org_permission_view') + auth = dict(HTTP_AUTHORIZATION=f"Bearer {token}") + base_org_permissions_url = reverse("test_base_org_permission_view") with self.assertRaises(NotImplementedError) as error: self.client.get(base_org_permissions_url, **auth) - self.assertIn('Please use one of the child classes', str(error.exception)) + self.assertIn("Please use one of the child classes", str(error.exception)) def test_organization_field_with_parent(self): operator = self._get_operator() self._create_org_user(user=operator) token = self._obtain_auth_token() - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') - response = self.client.get(reverse('test_organization_field_view'), **auth) + auth = dict(HTTP_AUTHORIZATION=f"Bearer {token}") + response = self.client.get(reverse("test_organization_field_view"), **auth) self.assertEqual(response.status_code, 200) def test_organization_field_with_errored_parent(self): operator = self._get_operator() self._create_org_user(user=operator) token = self._obtain_auth_token() - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') + auth = dict(HTTP_AUTHORIZATION=f"Bearer {token}") with self.assertRaises(AttributeError) as error: - self.client.get(reverse('test_error_field_view'), **auth) - self.assertIn('Organization not found', str(error.exception)) + self.client.get(reverse("test_error_field_view"), **auth) + self.assertIn("Organization not found", str(error.exception)) def _get_auth_template(self, user, org1): OrganizationUser.objects.create(user=user, organization=org1, is_admin=True) self.client.force_login(user) token = self._obtain_auth_token(user) - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') + auth = dict(HTTP_AUTHORIZATION=f"Bearer {token}") t1 = self._create_template(organization=org1) return (auth, t1) def test_view_permission_with_operator(self): user = self._get_user() - operator_group = Group.objects.filter(name='Operator') + operator_group = Group.objects.filter(name="Operator") user.groups.set(operator_group) org1 = self._get_org() auth, t1 = self._get_auth_template(user, org1) - with self.subTest('Get Template List'): - response = self.client.get(reverse('test_template_list'), **auth) + with self.subTest("Get Template List"): + response = self.client.get(reverse("test_template_list"), **auth) self.assertEqual(response.status_code, 403) - with self.subTest('Get Template Detail'): + with self.subTest("Get Template Detail"): response = self.client.get( - reverse('test_template_detail', args=[t1.pk]), **auth + reverse("test_template_detail", args=[t1.pk]), **auth ) self.assertEqual(response.status_code, 403) def test_view_permission_with_administrator(self): user = self._get_user() - administrator_group = Group.objects.get(name='Administrator') - change_perm = Permission.objects.get(codename='change_template') + administrator_group = Group.objects.get(name="Administrator") + change_perm = Permission.objects.get(codename="change_template") administrator_group.permissions.set([]) administrator_group.permissions.add(change_perm) user.groups.add(administrator_group) org1 = self._get_org() auth, t1 = self._get_auth_template(user, org1) - with self.subTest('Get Template List'): - response = self.client.get(reverse('test_template_list'), **auth) + with self.subTest("Get Template List"): + response = self.client.get(reverse("test_template_list"), **auth) self.assertEqual(response.status_code, 200) - with self.subTest('Get Template Detail'): + with self.subTest("Get Template Detail"): response = self.client.get( - reverse('test_template_detail', args=[t1.pk]), **auth + reverse("test_template_detail", args=[t1.pk]), **auth ) self.assertEqual(response.status_code, 200) - permissions = administrator_group.permissions.values_list('codename', flat=True) - self.assertFalse('view_template' in permissions) - self.assertTrue('change_template' in permissions) + permissions = administrator_group.permissions.values_list("codename", flat=True) + self.assertFalse("view_template" in permissions) + self.assertTrue("change_template" in permissions) def test_view_permission_with_operator_having_view_perm(self): user = self._get_user() - operator_group = Group.objects.get(name='Operator') - view_perm = Permission.objects.filter(codename='view_template') + operator_group = Group.objects.get(name="Operator") + view_perm = Permission.objects.filter(codename="view_template") operator_group.permissions.set(view_perm) user.groups.add(operator_group) org1 = self._get_org() auth, t1 = self._get_auth_template(user, org1) - with self.subTest('Get Template List'): - response = self.client.get(reverse('test_template_list'), **auth) + with self.subTest("Get Template List"): + response = self.client.get(reverse("test_template_list"), **auth) self.assertEqual(response.status_code, 200) - with self.subTest('Get Template Detail'): + with self.subTest("Get Template Detail"): response = self.client.get( - reverse('test_template_detail', args=[t1.pk]), **auth + reverse("test_template_detail", args=[t1.pk]), **auth ) self.assertEqual(response.status_code, 200) - with self.subTest('Change Template Detail'): - data = {'name': 'change-template'} + with self.subTest("Change Template Detail"): + data = {"name": "change-template"} response = self.client.patch( - reverse('test_template_detail', args=[t1.pk]), data, **auth + reverse("test_template_detail", args=[t1.pk]), data, **auth ) self.assertEqual(response.status_code, 403) - with self.subTest('Delete Template'): + with self.subTest("Delete Template"): response = self.client.delete( - reverse('test_template_detail', args=[t1.pk]), **auth + reverse("test_template_detail", args=[t1.pk]), **auth ) self.assertEqual(response.status_code, 403) def test_view_django_model_permission_with_view_perm(self): user = self._get_user() - user_permissions = Permission.objects.filter(codename='view_template') + user_permissions = Permission.objects.filter(codename="view_template") user.user_permissions.add(*user_permissions) user.organizations_dict # force caching org1 = self._get_org() auth, t1 = self._get_auth_template(user, org1) - with self.subTest('Get Template List'): - response = self.client.get(reverse('test_template_list'), **auth) + with self.subTest("Get Template List"): + response = self.client.get(reverse("test_template_list"), **auth) self.assertEqual(response.status_code, 200) - with self.subTest('Get Template Detail'): + with self.subTest("Get Template Detail"): response = self.client.get( - reverse('test_template_detail', args=[t1.pk]), **auth + reverse("test_template_detail", args=[t1.pk]), **auth ) self.assertEqual(response.status_code, 200) def test_view_django_model_permission_with_change_perm(self): user = self._get_user() - user_permissions = Permission.objects.filter(codename='change_template') + user_permissions = Permission.objects.filter(codename="change_template") user.user_permissions.add(*user_permissions) user.organizations_dict # force caching org1 = self._get_org() auth, t1 = self._get_auth_template(user, org1) - with self.subTest('Get Template List'): - response = self.client.get(reverse('test_template_list'), **auth) + with self.subTest("Get Template List"): + response = self.client.get(reverse("test_template_list"), **auth) self.assertEqual(response.status_code, 200) - with self.subTest('Get Template Detail'): + with self.subTest("Get Template Detail"): response = self.client.get( - reverse('test_template_detail', args=[t1.pk]), **auth + reverse("test_template_detail", args=[t1.pk]), **auth ) self.assertEqual(response.status_code, 200) def _test_access_shared_object( self, token, expected_templates_count=1, expected_status_codes={} ): - auth = dict(HTTP_AUTHORIZATION=f'Bearer {token}') + auth = dict(HTTP_AUTHORIZATION=f"Bearer {token}") template = self._create_template(organization=None) - with self.subTest('Test listing templates'): - response = self.client.get(reverse('test_template_list'), **auth) + with self.subTest("Test listing templates"): + response = self.client.get(reverse("test_template_list"), **auth) data = response.data.copy() # Only check "templates" in response. if isinstance(data, dict): - data.pop('detail', None) - self.assertEqual(response.status_code, expected_status_codes['list']) + data.pop("detail", None) + self.assertEqual(response.status_code, expected_status_codes["list"]) self.assertEqual(len(data), expected_templates_count) - with self.subTest('Test creating template'): + with self.subTest("Test creating template"): response = self.client.post( - reverse('test_template_list'), - data={'name': 'Test Template', 'organization': None}, - content_type='application/json', + reverse("test_template_list"), + data={"name": "Test Template", "organization": None}, + content_type="application/json", **auth, ) - self.assertEqual(response.status_code, expected_status_codes['create']) - if expected_status_codes['create'] == 400: + self.assertEqual(response.status_code, expected_status_codes["create"]) + if expected_status_codes["create"] == 400: self.assertEqual( - str(response.data['organization'][0]), 'This field may not be null.' + str(response.data["organization"][0]), "This field may not be null." ) - with self.subTest('Test retreiving template'): + with self.subTest("Test retreiving template"): response = self.client.get( - reverse('test_template_detail', args=[template.id]), **auth + reverse("test_template_detail", args=[template.id]), **auth ) - self.assertEqual(response.status_code, expected_status_codes['retrieve']) + self.assertEqual(response.status_code, expected_status_codes["retrieve"]) - with self.subTest('Test updating template'): + with self.subTest("Test updating template"): response = self.client.put( - reverse('test_template_detail', args=[template.id]), - data={'name': 'Name changed'}, - content_type='application/json', + reverse("test_template_detail", args=[template.id]), + data={"name": "Name changed"}, + content_type="application/json", **auth, ) - self.assertEqual(response.status_code, expected_status_codes['update']) + self.assertEqual(response.status_code, expected_status_codes["update"]) - with self.subTest('Test deleting template'): + with self.subTest("Test deleting template"): response = self.client.delete( - reverse('test_template_detail', args=[template.id]), **auth + reverse("test_template_detail", args=[template.id]), **auth ) - self.assertEqual(response.status_code, expected_status_codes['delete']) + self.assertEqual(response.status_code, expected_status_codes["delete"]) - with self.subTest('Test HEAD and OPTION methods'): - response = self.client.head(reverse('test_template_list'), **auth) - self.assertEqual(response.status_code, expected_status_codes['head']) + with self.subTest("Test HEAD and OPTION methods"): + response = self.client.head(reverse("test_template_list"), **auth) + self.assertEqual(response.status_code, expected_status_codes["head"]) - response = self.client.options(reverse('test_template_list'), **auth) - self.assertEqual(response.status_code, expected_status_codes['option']) + response = self.client.options(reverse("test_template_list"), **auth) + self.assertEqual(response.status_code, expected_status_codes["option"]) def test_superuser_access_shared_object(self): superuser = self._get_admin() @@ -293,13 +293,13 @@ def test_superuser_access_shared_object(self): self._test_access_shared_object( token, expected_status_codes={ - 'create': 201, - 'list': 200, - 'retrieve': 200, - 'update': 200, - 'delete': 204, - 'head': 200, - 'option': 200, + "create": 201, + "list": 200, + "retrieve": 200, + "update": 200, + "delete": 204, + "head": 200, + "option": 200, }, ) @@ -313,13 +313,13 @@ def test_org_manager_access_shared_object(self): self._test_access_shared_object( token, expected_status_codes={ - 'create': 400, - 'list': 200, - 'retrieve': 200, - 'update': 403, - 'delete': 403, - 'head': 200, - 'option': 200, + "create": 400, + "list": 200, + "retrieve": 200, + "update": 403, + "delete": 403, + "head": 200, + "option": 200, }, ) @@ -331,13 +331,13 @@ def test_org_owner_access_shared_object(self): self._test_access_shared_object( token, expected_status_codes={ - 'create': 400, - 'list': 200, - 'retrieve': 200, - 'update': 403, - 'delete': 403, - 'head': 200, - 'option': 200, + "create": 400, + "list": 200, + "retrieve": 200, + "update": 403, + "delete": 403, + "head": 200, + "option": 200, }, ) @@ -352,12 +352,12 @@ def test_org_user_access_shared_object(self): token, expected_templates_count=0, expected_status_codes={ - 'create': 400, - 'list': 200, - 'retrieve': 404, - 'update': 404, - 'delete': 404, - 'head': 200, - 'option': 200, + "create": 400, + "list": 200, + "retrieve": 404, + "update": 404, + "delete": 404, + "head": 200, + "option": 200, }, ) diff --git a/tests/testapp/tests/test_selenium.py b/tests/testapp/tests/test_selenium.py index 185a63247..4d99b71cd 100644 --- a/tests/testapp/tests/test_selenium.py +++ b/tests/testapp/tests/test_selenium.py @@ -12,7 +12,7 @@ from .mixins import TestMultitenancyMixin -Organization = load_model('openwisp_users', 'Organization') +Organization = load_model("openwisp_users", "Organization") class TestOrganizationAutocompleteField( @@ -29,116 +29,116 @@ def _test_multitenant_autocomplete_org_field( self.login(username=username, password=password) self.open(path) self.web_driver.find_element( - By.CSS_SELECTOR, '#select2-id_organization-container' + By.CSS_SELECTOR, "#select2-id_organization-container" ).click() WebDriverWait(self.web_driver, 2).until( EC.invisibility_of_element_located( - (By.CSS_SELECTOR, '.select2-results__option.loading-results') + (By.CSS_SELECTOR, ".select2-results__option.loading-results") ) ) options = self.web_driver.find_elements( - By.CSS_SELECTOR, '.select2-results__option' + By.CSS_SELECTOR, ".select2-results__option" ) for option in options: self.assertIn(option.text, visible) self.assertNotIn(option.text, hidden) def test_book_add_form_organization_field(self): - path = reverse('admin:testapp_book_add') - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') + path = reverse("admin:testapp_book_add") + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") administrator = self._create_administrator( - organizations=[org1], username='tester', password='tester' + organizations=[org1], username="tester", password="tester" ) administrator.user_permissions.add( *Permission.objects.filter( - Q(codename__contains='shelf') | Q(codename='view_organization') - ).values_list('id', flat=True), + Q(codename__contains="shelf") | Q(codename="view_organization") + ).values_list("id", flat=True), ) - with self.subTest('Test superuser'): + with self.subTest("Test superuser"): self._test_multitenant_autocomplete_org_field( path=path, username=self.admin_username, password=self.admin_password, - visible=Organization.objects.values_list('name', flat=True), + visible=Organization.objects.values_list("name", flat=True), hidden=[], ) self.logout() - with self.subTest('Test organization user: 1 org'): + with self.subTest("Test organization user: 1 org"): self._test_multitenant_autocomplete_org_field( path=path, - username='tester', - password='tester', + username="tester", + password="tester", visible=[org1.name], hidden=Organization.objects.exclude(id=org1.id).values_list( - 'name', flat=True + "name", flat=True ), ) org_select = Select( - self.web_driver.find_element(By.CSS_SELECTOR, '#id_organization') + self.web_driver.find_element(By.CSS_SELECTOR, "#id_organization") ) self.assertEqual(len(org_select.all_selected_options), 1) self.assertEqual(org_select.first_selected_option.text, org1.name) self.logout() - with self.subTest('Test organization user: 2 orgs'): + with self.subTest("Test organization user: 2 orgs"): self._create_org_user(user=administrator, organization=org2, is_admin=True) self._test_multitenant_autocomplete_org_field( path=path, - username='tester', - password='tester', + username="tester", + password="tester", visible=[org1.name, org2.name], hidden=Organization.objects.exclude( id__in=[org1.id, org2.id] - ).values_list('name', flat=True), + ).values_list("name", flat=True), ) org_select = Select( - self.web_driver.find_element(By.CSS_SELECTOR, '#id_organization') + self.web_driver.find_element(By.CSS_SELECTOR, "#id_organization") ) self.assertEqual(len(org_select.all_selected_options), 0) self.logout() def test_shelf_add_form_organization_field(self): - path = reverse('admin:testapp_shelf_add') - org1 = self._create_org(name='org1') + path = reverse("admin:testapp_shelf_add") + org1 = self._create_org(name="org1") administrator = self._create_administrator( - organizations=[org1], username='tester', password='tester' + organizations=[org1], username="tester", password="tester" ) administrator.user_permissions.add( *Permission.objects.filter( - Q(codename__contains='shelf') | Q(codename='view_organization') - ).values_list('id', flat=True), + Q(codename__contains="shelf") | Q(codename="view_organization") + ).values_list("id", flat=True), ) - with self.subTest('Test superuser'): + with self.subTest("Test superuser"): self._test_multitenant_autocomplete_org_field( path=path, username=self.admin_username, password=self.admin_password, - visible=list(Organization.objects.values_list('name', flat=True)) - + ['Shared systemwide (no organization)'], + visible=list(Organization.objects.values_list("name", flat=True)) + + ["Shared systemwide (no organization)"], hidden=[], ) self.logout() - with self.subTest('Test organization user'): + with self.subTest("Test organization user"): self._test_multitenant_autocomplete_org_field( path=path, - username='tester', - password='tester', + username="tester", + password="tester", visible=[org1.name], hidden=list( Organization.objects.exclude(id=org1.id).values_list( - 'name', flat=True + "name", flat=True ) ) - + ['Shared systemwide (no organization)'], + + ["Shared systemwide (no organization)"], ) org_select = Select( - self.web_driver.find_element(By.CSS_SELECTOR, '#id_organization') + self.web_driver.find_element(By.CSS_SELECTOR, "#id_organization") ) self.assertEqual(len(org_select.all_selected_options), 1) self.assertEqual(org_select.first_selected_option.text, org1.name) diff --git a/tests/testapp/tests/test_views.py b/tests/testapp/tests/test_views.py index dbe12a012..f72e1e038 100644 --- a/tests/testapp/tests/test_views.py +++ b/tests/testapp/tests/test_views.py @@ -8,18 +8,18 @@ from openwisp_users.tests.utils import TestMultitenantAdminMixin from openwisp_utils.tests import capture_stderr -Organization = load_model('openwisp_users', 'Organization') -OrganizationUser = load_model('openwisp_users', 'OrganizationUser') +Organization = load_model("openwisp_users", "Organization") +OrganizationUser = load_model("openwisp_users", "OrganizationUser") class TestAutocompleteJsonView(TestMultitenantAdminMixin, TestCase): def test_autocomplete_view_organization_filter(self): - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") self._get_admin() self._create_administrator(organizations=[org1]) self._test_multitenant_admin( - url=self._get_autocomplete_view_path('testapp', 'book', 'organization'), + url=self._get_autocomplete_view_path("testapp", "book", "organization"), visible=[org1.name], hidden=[org2.name], administrator=True, @@ -29,53 +29,53 @@ def test_autocomplete_view_organization_filter(self): def test_autocomplete_view_blank_option(self): admin = self._get_admin() self.client.force_login(admin) - path = reverse('admin:ow-auto-filter') - with self.subTest('Test for Book.shelf filter'): + path = reverse("admin:ow-auto-filter") + with self.subTest("Test for Book.shelf filter"): response = self.client.get( path, data={ - 'app_label': 'testapp', - 'model_name': 'book', - 'field_name': 'shelf', + "app_label": "testapp", + "model_name": "book", + "field_name": "shelf", }, ) - for option in response.json()['results']: - if option['id'] == 'null': - self.assertEqual(option['text'], '-') + for option in response.json()["results"]: + if option["id"] == "null": + self.assertEqual(option["text"], "-") break else: - self.fail('Null option not found in response') + self.fail("Null option not found in response") - with self.subTest('Test for Shelf.organization filter'): + with self.subTest("Test for Shelf.organization filter"): response = self.client.get( path, data={ - 'app_label': 'testapp', - 'model_name': 'shelf', - 'field_name': 'organization', + "app_label": "testapp", + "model_name": "shelf", + "field_name": "organization", }, ) - for option in response.json()['results']: - if option['id'] == 'null': + for option in response.json()["results"]: + if option["id"] == "null": self.assertEqual( - option['text'], 'Shared systemwide (no organization)' + option["text"], "Shared systemwide (no organization)" ) break else: - self.fail('Null option not found in response') + self.fail("Null option not found in response") def test_autocomplete_view_for_inline_admin(self): admin = self._get_admin() self.client.force_login(admin) - path = reverse('admin:ow-auto-filter') - with patch.object(site, '_registry', site._registry) as mocked_registry: + path = reverse("admin:ow-auto-filter") + with patch.object(site, "_registry", site._registry) as mocked_registry: mocked_registry.pop(OrganizationUser) response = self.client.get( path, data={ - 'app_label': OrganizationUser._meta.app_label, - 'model_name': OrganizationUser._meta.model_name, - 'field_name': 'organization', + "app_label": OrganizationUser._meta.app_label, + "model_name": OrganizationUser._meta.model_name, + "field_name": "organization", }, ) - self.assertEqual(len(response.json()['results']), Organization.objects.count()) + self.assertEqual(len(response.json()["results"]), Organization.objects.count()) diff --git a/tests/testapp/tests/tests.py b/tests/testapp/tests/tests.py index ae539aff6..79f936873 100644 --- a/tests/testapp/tests/tests.py +++ b/tests/testapp/tests/tests.py @@ -10,86 +10,86 @@ class TestIntegration(TestOrganizationMixin, TestCase): def test_derived_model_config(self): self.assertEqual(Template.objects.count(), 0) - t = Template(name='test') + t = Template(name="test") t.full_clean() t.save() self.assertEqual(Template.objects.count(), 1) def test_derived_model_template(self): - c = Config(name='test') + c = Config(name="test") with self.assertRaises(ValidationError): c.full_clean() def test_validate_org_relation_pk_comparison_bug(self): self.assertEqual(Config.objects.count(), 0) org = self._create_org() - t = Template.objects.create(name='test', organization=org) - c = Config(name='test', template=t, organization_id=str(org.pk)) + t = Template.objects.create(name="test", organization=org) + c = Config(name="test", template=t, organization_id=str(org.pk)) c.full_clean() c.save() self.assertEqual(Config.objects.count(), 1) def test_validate_org_relation(self): - c = Config(name='test') + c = Config(name="test") # simulates validating a relation instance attribute that has not been set yet - self.assertEqual(c._validate_org_relation('not_set_yet'), None) + self.assertEqual(c._validate_org_relation("not_set_yet"), None) def test_validate_org_relation_error(self): org = self._create_org() - t = Template.objects.create(name='test', organization=org) - c = Config(name='test', template=t) + t = Template.objects.create(name="test", organization=org) + c = Config(name="test", template=t) with self.assertRaises(ValidationError): c.full_clean() def test_validate_reverse_org_relation(self): - org1 = self._create_org(name='org1') - org2 = self._create_org(name='org2') - t = Template.objects.create(name='test-t', organization=org1) - Config.objects.create(name='test-c1', template=t, organization=org1) + org1 = self._create_org(name="org1") + org2 = self._create_org(name="org2") + t = Template.objects.create(name="test-t", organization=org1) + Config.objects.create(name="test-c1", template=t, organization=org1) with self.assertRaises(ValidationError): t.organization = org2 t.full_clean() def test_validate_reverse_org_relation_return(self): - t = Template(name='test-t') + t = Template(name="test-t") t.full_clean() - org1 = self._create_org(name='org1') - t = Template.objects.create(name='test-t', organization=org1) - t.name = 'test-template' + org1 = self._create_org(name="org1") + t = Template.objects.create(name="test-t", organization=org1) + t.name = "test-template" t.full_clean() def test_resolve_account_URLs(self): - resolver = resolve('/accounts/login/') - self.assertEqual(resolver.view_name, 'account_login') - resolver = resolve('/accounts/signup/') - self.assertEqual(resolver.view_name, 'account_signup') - resolver = resolve('/accounts/logout/') - self.assertEqual(resolver.view_name, 'account_logout') - resolver = resolve('/accounts/password/reset/') - self.assertEqual(resolver.view_name, 'account_reset_password') - resolver = resolve('/accounts/password/change/') - self.assertEqual(resolver.view_name, 'account_change_password') + resolver = resolve("/accounts/login/") + self.assertEqual(resolver.view_name, "account_login") + resolver = resolve("/accounts/signup/") + self.assertEqual(resolver.view_name, "account_signup") + resolver = resolve("/accounts/logout/") + self.assertEqual(resolver.view_name, "account_logout") + resolver = resolve("/accounts/password/reset/") + self.assertEqual(resolver.view_name, "account_reset_password") + resolver = resolve("/accounts/password/change/") + self.assertEqual(resolver.view_name, "account_change_password") def test_account_email_verification_sent(self): - r = self.client.get(reverse('account_email_verification_sent')) + r = self.client.get(reverse("account_email_verification_sent")) self.assertEqual(r.status_code, 200) - self.assertNotContains(r, 'Change E-mail
    ') - self.assertNotContains(r, 'Sign Up') + self.assertNotContains(r, "Change E-mail") + self.assertNotContains(r, "Sign Up") def test_account_confirm_email(self): - r = self.client.get(reverse('account_confirm_email', args=['abc123'])) + r = self.client.get(reverse("account_confirm_email", args=["abc123"])) self.assertEqual(r.status_code, 200) - self.assertNotContains(r, 'Change E-mail') - self.assertNotContains(r, 'Sign Up') + self.assertNotContains(r, "Change E-mail") + self.assertNotContains(r, "Sign Up") def test_account_reset_password(self): - r = self.client.get(reverse('account_reset_password')) + r = self.client.get(reverse("account_reset_password")) self.assertEqual(r.status_code, 200) - self.assertNotContains(r, 'Change E-mail') - self.assertNotContains(r, 'Sign Up') + self.assertNotContains(r, "Change E-mail") + self.assertNotContains(r, "Sign Up") def test_account_change_password(self): - response = self.client.get(reverse('account_change_password')) + response = self.client.get(reverse("account_change_password")) self.assertRedirects( - response, '/accounts/login/?next=/accounts/password/change/' + response, "/accounts/login/?next=/accounts/password/change/" ) diff --git a/tests/testapp/urls.py b/tests/testapp/urls.py index 370b8d521..bf285fc39 100644 --- a/tests/testapp/urls.py +++ b/tests/testapp/urls.py @@ -3,80 +3,80 @@ from . import views urlpatterns = [ - path('member_view', views.api_member_view, name='test_api_member_view'), - path('manager_view', views.api_manager_view, name='test_api_manager_view'), - path('owner_view', views.api_owner_view, name='test_api_owner_view'), - path('base_org_view', views.base_org_view, name='test_base_org_permission_view'), - path('org_field_view', views.org_field_view, name='test_organization_field_view'), - path('error_field_view', views.error_field_view, name='test_error_field_view'), + path("member_view", views.api_member_view, name="test_api_member_view"), + path("manager_view", views.api_manager_view, name="test_api_manager_view"), + path("owner_view", views.api_owner_view, name="test_api_owner_view"), + path("base_org_view", views.base_org_view, name="test_base_org_permission_view"), + path("org_field_view", views.org_field_view, name="test_organization_field_view"), + path("error_field_view", views.error_field_view, name="test_error_field_view"), path( - 'member/shelf//books', + "member/shelf//books", views.books_list_member_view, - name='test_books_list_member_view', + name="test_books_list_member_view", ), path( - 'manager/shelf//books', + "manager/shelf//books", views.books_list_manager_view, - name='test_books_list_manager_view', + name="test_books_list_manager_view", ), path( - 'owner/shelf//books', + "owner/shelf//books", views.books_list_owner_view, - name='test_books_list_owner_view', + name="test_books_list_owner_view", ), path( - 'member/shelf', + "member/shelf", views.shelf_list_member_view, - name='test_shelf_list_member_view', + name="test_shelf_list_member_view", ), path( - 'manager/shelf', + "manager/shelf", views.shelf_list_manager_view, - name='test_shelf_list_manager_view', + name="test_shelf_list_manager_view", ), path( - 'owner/shelf', + "owner/shelf", views.shelf_list_owner_view, - name='test_shelf_list_owner_view', + name="test_shelf_list_owner_view", ), path( - 'user/shelf//books', + "user/shelf//books", views.book_list_unauthorized_view, - name='test_book_list_unauthorized_view', + name="test_book_list_unauthorized_view", ), path( - 'user/shelf', + "user/shelf", views.shelf_list_unauthorized_view, - name='test_shelf_list_unauthorized_view', + name="test_shelf_list_unauthorized_view", ), path( - 'template/', + "template/", views.template_list, - name='test_template_list', + name="test_template_list", ), path( - 'template//', + "template//", views.template_detail, - name='test_template_detail', + name="test_template_detail", ), path( - 'library/', + "library/", views.library_list, - name='test_library_list', + name="test_library_list", ), path( - 'library//', + "library//", views.library_detail, - name='test_library_detail', + name="test_library_detail", ), path( - 'booknestedshelf/', + "booknestedshelf/", views.book_nested_shelf, - name='test_book_nested_shelf', + name="test_book_nested_shelf", ), path( - 'shelfreadonlyorg/', + "shelfreadonlyorg/", views.shelf_with_read_only_org_view, - name='test_shelf_list_with_read_only_org', + name="test_shelf_list_with_read_only_org", ), ] diff --git a/tests/testapp/views.py b/tests/testapp/views.py index 211cfa632..77d2a7994 100644 --- a/tests/testapp/views.py +++ b/tests/testapp/views.py @@ -44,12 +44,12 @@ TemplateSerializer, ) -Organization = swapper.load_model('openwisp_users', 'Organization') +Organization = swapper.load_model("openwisp_users", "Organization") class BookOrgMixin: def get_parent_queryset(self): - shelf_org = Shelf.objects.get(pk=self.kwargs['shelf_id']).organization + shelf_org = Shelf.objects.get(pk=self.kwargs["shelf_id"]).organization qs = Book.objects.filter(organization=shelf_org.pk) return qs @@ -58,9 +58,9 @@ class BaseGetApiView(APIView): queryset = Template.objects.all() def get(self, request, *args, **kwargs): - testorg, _ = Organization.objects.get_or_create(name='test org') + testorg, _ = Organization.objects.get_or_create(name="test org") template, _ = Template.objects.get_or_create( - name='sample', organization=testorg + name="sample", organization=testorg ) self.check_object_permissions(request, template) return Response({}) @@ -90,11 +90,11 @@ class OrganizationFieldView(APIView): queryset = Config.objects.all() authentication_classes = (BearerAuthentication,) permission_classes = (IsOrganizationMember,) - organization_field = 'template__organization' + organization_field = "template__organization" def get(self, request, *args, **kwargs): - testorg, _ = Organization.objects.get_or_create(name='test org') - temp1, _ = Template.objects.get_or_create(name='temp', organization=testorg) + testorg, _ = Organization.objects.get_or_create(name="test org") + temp1, _ = Template.objects.get_or_create(name="temp", organization=testorg) config, _ = Config.objects.get_or_create(template=temp1, organization=testorg) self.check_object_permissions(request, config) return Response({}) @@ -103,13 +103,13 @@ def get(self, request, *args, **kwargs): class ErrorOrganizationFieldView(BaseGetApiView): authentication_classes = (BearerAuthentication,) permission_classes = (IsOrganizationMember,) - organization_field = 'error__organization' + organization_field = "error__organization" class ShelfListMemberFilter(OrganizationMembershipFilter): class Meta(OrganizationMembershipFilter.Meta): model = Shelf - fields = OrganizationMembershipFilter.Meta.fields + ['tags'] + fields = OrganizationMembershipFilter.Meta.fields + ["tags"] class ShelfListMemberView(FilterByOrganizationMembership, ListAPIView): @@ -124,7 +124,7 @@ class ShelfListMemberView(FilterByOrganizationMembership, ListAPIView): class ShelfListManagerFilter(OrganizationManagedFilter): class Meta(OrganizationManagedFilter.Meta): model = Shelf - fields = OrganizationManagedFilter.Meta.fields + ['tags'] + fields = OrganizationManagedFilter.Meta.fields + ["tags"] class ShelfListManagerView(FilterByOrganizationManaged, ListAPIView): @@ -139,7 +139,7 @@ class ShelfListManagerView(FilterByOrganizationManaged, ListAPIView): class ShelfListOwnerFilter(OrganizationOwnedFilter): class Meta(OrganizationOwnedFilter.Meta): model = Shelf - fields = OrganizationOwnedFilter.Meta.fields + ['tags'] + fields = OrganizationOwnedFilter.Meta.fields + ["tags"] class ShelfListOwnerView(FilterByOrganizationOwned, ListAPIView): @@ -158,7 +158,7 @@ class BooksListMemberView(BookOrgMixin, FilterByParentMembership, ListCreateAPIV serializer_class = BookMemberSerializer def get_queryset(self): - shelf = Shelf.objects.get(pk=self.kwargs['shelf_id']) + shelf = Shelf.objects.get(pk=self.kwargs["shelf_id"]) super().get_queryset() return shelf.book_set.all() @@ -170,7 +170,7 @@ class BooksListManagerView(BookOrgMixin, FilterByParentManaged, ListCreateAPIVie serializer_class = BookManagerSerializer def get_queryset(self): - shelf = Shelf.objects.get(pk=self.kwargs['shelf_id']) + shelf = Shelf.objects.get(pk=self.kwargs["shelf_id"]) super().get_queryset() return shelf.book_set.all() @@ -182,7 +182,7 @@ class BooksListOwnerView(BookOrgMixin, FilterByParentOwned, ListCreateAPIView): serializer_class = BookOwnerSerializer def get_queryset(self): - shelf = Shelf.objects.get(pk=self.kwargs['shelf_id']) + shelf = Shelf.objects.get(pk=self.kwargs["shelf_id"]) super().get_queryset() return shelf.book_set.all() @@ -200,7 +200,7 @@ class BooksListUnauthorizedView(BookOrgMixin, FilterByParentOwned, ListAPIView): serializer_class = BookSerializer def get_queryset(self): - shelf = Shelf.objects.get(pk=self.kwargs['shelf_id']) + shelf = Shelf.objects.get(pk=self.kwargs["shelf_id"]) super().get_queryset() return shelf.book_set.all() @@ -229,14 +229,14 @@ class LibraryListFilter(FilterDjangoByOrgManaged): class Meta: model = Library fields = ( - 'book', - 'book__organization', + "book", + "book__organization", ) class LibraryListCreateView(FilterByOrganizationManaged, ListCreateAPIView): serializer_class = LibrarySerializer - organization_field = 'book__organization' + organization_field = "book__organization" authentication_classes = (BearerAuthentication,) permission_classes = (IsOrganizationMember,) queryset = Library.objects.all() @@ -246,7 +246,7 @@ class LibraryListCreateView(FilterByOrganizationManaged, ListCreateAPIView): class LibraryDetailView(FilterByOrganizationManaged, RetrieveUpdateDestroyAPIView): serializer_class = LibrarySerializer - organization_field = 'book__organization' + organization_field = "book__organization" authentication_classes = (BearerAuthentication,) permission_classes = (IsOrganizationMember,) queryset = Library.objects.all()