BmorricalDjangoWorkflows 2.1.1__tar.gz → 2.1.2__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/BmorricalDjangoWorkflows.egg-info/PKG-INFO +1 -1
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/PKG-INFO +1 -1
- bmorricaldjangoworkflows-2.1.2/VERSION +1 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/users/services/auth_flow.py +9 -2
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/users/views.py +47 -8
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/tests/test_auth_flow.py +24 -1
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/tests/test_user_views.py +113 -9
- bmorricaldjangoworkflows-2.1.1/VERSION +0 -1
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/BmorricalDjangoWorkflows.egg-info/SOURCES.txt +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/BmorricalDjangoWorkflows.egg-info/dependency_links.txt +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/BmorricalDjangoWorkflows.egg-info/requires.txt +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/BmorricalDjangoWorkflows.egg-info/top_level.txt +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/LICENSE +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/MANIFEST.in +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/README.md +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/__init__.py +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/services/__init__.py +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/services/email.py +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/users/__init__.py +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/users/services/__init__.py +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/users/urls.py +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/setup.cfg +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/setup.py +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/tests/__init__.py +0 -0
- {bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/tests/test_email.py +0 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.1.2
|
|
@@ -342,8 +342,8 @@ def list_users(*, exclude_groups: Any = None) -> list[dict[str, Any]]:
|
|
|
342
342
|
users = sorted(
|
|
343
343
|
filtered_users,
|
|
344
344
|
key=lambda user: (
|
|
345
|
-
str(getattr(user, "username", "") or "").lower(),
|
|
346
345
|
not bool(getattr(user, "is_active", False)),
|
|
346
|
+
str(getattr(user, "username", "") or "").lower(),
|
|
347
347
|
),
|
|
348
348
|
)
|
|
349
349
|
|
|
@@ -639,7 +639,14 @@ def update_user(user: Any, data: dict[str, Any]) -> UserUpdateResult:
|
|
|
639
639
|
_sync_user_groups(user=user, groups=validated_groups)
|
|
640
640
|
|
|
641
641
|
if user_fields_updated:
|
|
642
|
-
|
|
642
|
+
try:
|
|
643
|
+
user.save()
|
|
644
|
+
except IntegrityError:
|
|
645
|
+
logger.exception("User update failed due to IntegrityError for email=%s", user.email)
|
|
646
|
+
return UserUpdateResult(
|
|
647
|
+
success=False,
|
|
648
|
+
message="A user with this email already exists.",
|
|
649
|
+
)
|
|
643
650
|
|
|
644
651
|
if not processed_update:
|
|
645
652
|
return UserUpdateResult(
|
{bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/users/views.py
RENAMED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import logging
|
|
2
2
|
|
|
3
|
+
from django.contrib.auth import get_user_model
|
|
4
|
+
|
|
3
5
|
from rest_framework import status
|
|
4
6
|
from rest_framework.permissions import BasePermission, IsAuthenticated
|
|
5
7
|
from rest_framework.response import Response
|
|
@@ -38,11 +40,43 @@ def _user_has_group(user, group_name):
|
|
|
38
40
|
return False
|
|
39
41
|
|
|
40
42
|
|
|
41
|
-
class
|
|
42
|
-
message = "
|
|
43
|
+
class HasUserManagerGroup(BasePermission):
|
|
44
|
+
message = "User Manager group membership is required."
|
|
43
45
|
|
|
44
46
|
def has_permission(self, request, view):
|
|
45
|
-
return _user_has_group(request.user, "
|
|
47
|
+
return _user_has_group(request.user, "User Manager")
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _resolve_patch_target_user(request):
|
|
51
|
+
target_user_id = request.data.get("id")
|
|
52
|
+
if target_user_id is None:
|
|
53
|
+
return request.user, None
|
|
54
|
+
|
|
55
|
+
try:
|
|
56
|
+
target_user_id = int(target_user_id)
|
|
57
|
+
except (TypeError, ValueError):
|
|
58
|
+
return None, Response(
|
|
59
|
+
{"detail": "id must be an integer value."},
|
|
60
|
+
status=status.HTTP_400_BAD_REQUEST,
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
if target_user_id == getattr(request.user, "id", None):
|
|
64
|
+
return request.user, None
|
|
65
|
+
|
|
66
|
+
if not _user_has_group(request.user, "User Manager"):
|
|
67
|
+
return None, Response(
|
|
68
|
+
{"detail": HasUserManagerGroup.message},
|
|
69
|
+
status=status.HTTP_403_FORBIDDEN,
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
User = get_user_model()
|
|
73
|
+
try:
|
|
74
|
+
return User.objects.get(id=target_user_id), None
|
|
75
|
+
except User.DoesNotExist:
|
|
76
|
+
return None, Response(
|
|
77
|
+
{"detail": "User not found."},
|
|
78
|
+
status=status.HTTP_404_NOT_FOUND,
|
|
79
|
+
)
|
|
46
80
|
|
|
47
81
|
|
|
48
82
|
class AnonymousWorkflowView(APIView):
|
|
@@ -124,11 +158,11 @@ class UserCreateView(AnonymousWorkflowView):
|
|
|
124
158
|
|
|
125
159
|
|
|
126
160
|
# Route: POST /admin-register/
|
|
127
|
-
# Access: authenticated users in the
|
|
161
|
+
# Access: authenticated users in the User Manager group
|
|
128
162
|
# Body params: firstName, lastName, email, optional username, optional company honeypot field, optional groups as [{name, enabled}]
|
|
129
163
|
# Notes: server generates a strong password, creates an active user, and sends an informational email without verification code logic
|
|
130
164
|
class AdminUserCreateView(APIView):
|
|
131
|
-
permission_classes = [IsAuthenticated,
|
|
165
|
+
permission_classes = [IsAuthenticated, HasUserManagerGroup]
|
|
132
166
|
|
|
133
167
|
def post(self, request):
|
|
134
168
|
data = request.data
|
|
@@ -242,7 +276,8 @@ class UserListView(APIView):
|
|
|
242
276
|
|
|
243
277
|
# Route: GET /user/
|
|
244
278
|
# Route: PATCH /user/
|
|
245
|
-
# PATCH body params: username, email, firstName, lastName, password, isActive, optional groups as [{name, enabled}]
|
|
279
|
+
# PATCH body params: optional id, username, email, firstName, lastName, password, isActive, optional groups as [{name, enabled}]
|
|
280
|
+
# Notes: when id is omitted the authenticated user is updated; updating another user by id requires the User Manager group
|
|
246
281
|
class UserDetailView(APIView):
|
|
247
282
|
permission_classes = [IsAuthenticated]
|
|
248
283
|
|
|
@@ -252,7 +287,11 @@ class UserDetailView(APIView):
|
|
|
252
287
|
return Response(data, status=status.HTTP_200_OK)
|
|
253
288
|
|
|
254
289
|
def patch(self, request):
|
|
255
|
-
|
|
290
|
+
target_user, error_response = _resolve_patch_target_user(request)
|
|
291
|
+
if error_response is not None:
|
|
292
|
+
return error_response
|
|
293
|
+
|
|
294
|
+
result = update_user(target_user, request.data)
|
|
256
295
|
|
|
257
296
|
if result.success:
|
|
258
297
|
return Response(
|
|
@@ -260,5 +299,5 @@ class UserDetailView(APIView):
|
|
|
260
299
|
status=status.HTTP_200_OK,
|
|
261
300
|
)
|
|
262
301
|
|
|
263
|
-
logger.error("UserDetailView: %s for user %s", result.message,
|
|
302
|
+
logger.error("UserDetailView: %s for user %s", result.message, target_user.email)
|
|
264
303
|
return Response({"detail": result.message}, status=status.HTTP_400_BAD_REQUEST)
|
|
@@ -376,8 +376,8 @@ class AuthFlowTests(TestCase):
|
|
|
376
376
|
[(item["username"], item["is_active"], item["email"]) for item in result],
|
|
377
377
|
[
|
|
378
378
|
("alex@example.com", True, "alex-active@example.com"),
|
|
379
|
-
("alex@example.com", False, "alex-inactive@example.com"),
|
|
380
379
|
("bravo@example.com", True, "bravo@example.com"),
|
|
380
|
+
("alex@example.com", False, "alex-inactive@example.com"),
|
|
381
381
|
],
|
|
382
382
|
)
|
|
383
383
|
|
|
@@ -1607,6 +1607,29 @@ class AuthFlowTests(TestCase):
|
|
|
1607
1607
|
self.assertEqual(result.message, "groups must be a list of objects with name and enabled.")
|
|
1608
1608
|
user.save.assert_not_called()
|
|
1609
1609
|
|
|
1610
|
+
def test_update_user_returns_conflict_when_save_hits_integrity_error(self):
|
|
1611
|
+
user = SimpleNamespace(
|
|
1612
|
+
username="member@example.com",
|
|
1613
|
+
email="member@example.com",
|
|
1614
|
+
first_name="Member",
|
|
1615
|
+
last_name="User",
|
|
1616
|
+
is_active=True,
|
|
1617
|
+
groups=SimpleNamespace(add=MagicMock(), remove=MagicMock()),
|
|
1618
|
+
save=MagicMock(side_effect=auth_flow.IntegrityError("duplicate")),
|
|
1619
|
+
set_password=MagicMock(),
|
|
1620
|
+
)
|
|
1621
|
+
|
|
1622
|
+
result = auth_flow.update_user(
|
|
1623
|
+
user,
|
|
1624
|
+
{
|
|
1625
|
+
"email": "taken@example.com",
|
|
1626
|
+
"username": "taken@example.com",
|
|
1627
|
+
},
|
|
1628
|
+
)
|
|
1629
|
+
|
|
1630
|
+
self.assertFalse(result.success)
|
|
1631
|
+
self.assertEqual(result.message, "A user with this email already exists.")
|
|
1632
|
+
|
|
1610
1633
|
def test_update_user_returns_error_for_empty_payload(self):
|
|
1611
1634
|
user = SimpleNamespace(
|
|
1612
1635
|
username="member@example.com",
|
|
@@ -349,6 +349,110 @@ class PackageUserDetailViewTest(SimpleTestCase):
|
|
|
349
349
|
},
|
|
350
350
|
)
|
|
351
351
|
|
|
352
|
+
@patch("django_workflows.users.views.get_user_model")
|
|
353
|
+
@patch("django_workflows.users.views.update_user")
|
|
354
|
+
def test_user_detail_patch_uses_payload_id_for_time_entry_admin(self, mock_update_user, mock_get_user_model):
|
|
355
|
+
mock_update_user.return_value = SimpleNamespace(
|
|
356
|
+
success=True,
|
|
357
|
+
message="User updated successfully.",
|
|
358
|
+
)
|
|
359
|
+
|
|
360
|
+
actor = self._make_user()
|
|
361
|
+
actor.id = 1
|
|
362
|
+
actor.groups = SimpleNamespace(all=lambda: [SimpleNamespace(name="User Manager")])
|
|
363
|
+
target_user = self._make_user()
|
|
364
|
+
target_user.id = 9
|
|
365
|
+
target_user.email = "target@example.com"
|
|
366
|
+
target_user.username = "target@example.com"
|
|
367
|
+
|
|
368
|
+
user_model = SimpleNamespace(objects=SimpleNamespace(get=lambda **kwargs: target_user), DoesNotExist=Exception)
|
|
369
|
+
mock_get_user_model.return_value = user_model
|
|
370
|
+
|
|
371
|
+
request = SimpleNamespace(
|
|
372
|
+
user=actor,
|
|
373
|
+
data={
|
|
374
|
+
"id": 9,
|
|
375
|
+
"email": "target@example.com",
|
|
376
|
+
"username": "target@example.com",
|
|
377
|
+
"isActive": 1,
|
|
378
|
+
"groups": [{"name": "Time Entry User", "enabled": True}],
|
|
379
|
+
},
|
|
380
|
+
)
|
|
381
|
+
response = workflow_views.UserDetailView().patch(request)
|
|
382
|
+
|
|
383
|
+
self.assertEqual(response.status_code, 200)
|
|
384
|
+
mock_update_user.assert_called_once_with(target_user, request.data)
|
|
385
|
+
|
|
386
|
+
@patch("django_workflows.users.views.get_user_model")
|
|
387
|
+
@patch("django_workflows.users.views.update_user")
|
|
388
|
+
def test_user_detail_patch_uses_authenticated_user_when_payload_id_matches_self(
|
|
389
|
+
self,
|
|
390
|
+
mock_update_user,
|
|
391
|
+
mock_get_user_model,
|
|
392
|
+
):
|
|
393
|
+
mock_update_user.return_value = SimpleNamespace(
|
|
394
|
+
success=True,
|
|
395
|
+
message="User updated successfully.",
|
|
396
|
+
)
|
|
397
|
+
|
|
398
|
+
actor = self._make_user()
|
|
399
|
+
actor.id = 9
|
|
400
|
+
request = SimpleNamespace(
|
|
401
|
+
user=actor,
|
|
402
|
+
data={
|
|
403
|
+
"id": 9,
|
|
404
|
+
"email": "member@example.com",
|
|
405
|
+
"username": "member@example.com",
|
|
406
|
+
"firstName": "Updated",
|
|
407
|
+
},
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
response = workflow_views.UserDetailView().patch(request)
|
|
411
|
+
|
|
412
|
+
self.assertEqual(response.status_code, 200)
|
|
413
|
+
mock_update_user.assert_called_once_with(actor, request.data)
|
|
414
|
+
mock_get_user_model.assert_not_called()
|
|
415
|
+
|
|
416
|
+
def test_user_detail_patch_returns_400_for_invalid_id(self):
|
|
417
|
+
request = SimpleNamespace(user=self._make_user(), data={"id": "not-an-int"})
|
|
418
|
+
|
|
419
|
+
response = workflow_views.UserDetailView().patch(request)
|
|
420
|
+
|
|
421
|
+
self.assertEqual(response.status_code, 400)
|
|
422
|
+
self.assertEqual(response.data["detail"], "id must be an integer value.")
|
|
423
|
+
|
|
424
|
+
def test_user_detail_patch_returns_403_for_foreign_id_without_time_entry_admin(self):
|
|
425
|
+
actor = self._make_user()
|
|
426
|
+
actor.id = 1
|
|
427
|
+
actor.groups = SimpleNamespace(all=lambda: [SimpleNamespace(name="Time Entry User")])
|
|
428
|
+
request = SimpleNamespace(user=actor, data={"id": 9, "firstName": "Updated"})
|
|
429
|
+
|
|
430
|
+
response = workflow_views.UserDetailView().patch(request)
|
|
431
|
+
|
|
432
|
+
self.assertEqual(response.status_code, 403)
|
|
433
|
+
self.assertEqual(response.data["detail"], workflow_views.HasUserManagerGroup.message)
|
|
434
|
+
|
|
435
|
+
@patch("django_workflows.users.views.get_user_model")
|
|
436
|
+
def test_user_detail_patch_returns_404_when_target_id_is_missing(self, mock_get_user_model):
|
|
437
|
+
class DoesNotExist(Exception):
|
|
438
|
+
pass
|
|
439
|
+
|
|
440
|
+
actor = self._make_user()
|
|
441
|
+
actor.id = 1
|
|
442
|
+
actor.groups = SimpleNamespace(all=lambda: [SimpleNamespace(name="User Manager")])
|
|
443
|
+
|
|
444
|
+
user_model = SimpleNamespace(
|
|
445
|
+
objects=SimpleNamespace(get=lambda **kwargs: (_ for _ in ()).throw(DoesNotExist())),
|
|
446
|
+
DoesNotExist=DoesNotExist,
|
|
447
|
+
)
|
|
448
|
+
mock_get_user_model.return_value = user_model
|
|
449
|
+
|
|
450
|
+
request = SimpleNamespace(user=actor, data={"id": 99, "firstName": "Updated"})
|
|
451
|
+
response = workflow_views.UserDetailView().patch(request)
|
|
452
|
+
|
|
453
|
+
self.assertEqual(response.status_code, 404)
|
|
454
|
+
self.assertEqual(response.data["detail"], "User not found.")
|
|
455
|
+
|
|
352
456
|
@patch("django_workflows.users.views.update_user")
|
|
353
457
|
def test_user_detail_patch_validation_error_returns_400(self, mock_update_user):
|
|
354
458
|
mock_update_user.return_value = SimpleNamespace(
|
|
@@ -424,7 +528,7 @@ class PackageAdminUserWorkflowViewsTest(SimpleTestCase):
|
|
|
424
528
|
},
|
|
425
529
|
format="json",
|
|
426
530
|
)
|
|
427
|
-
force_authenticate(request, user=self._make_authenticated_user(["
|
|
531
|
+
force_authenticate(request, user=self._make_authenticated_user(["User Manager"]))
|
|
428
532
|
|
|
429
533
|
response = workflow_views.AdminUserCreateView.as_view()(request)
|
|
430
534
|
|
|
@@ -467,7 +571,7 @@ class PackageAdminUserWorkflowViewsTest(SimpleTestCase):
|
|
|
467
571
|
},
|
|
468
572
|
format="json",
|
|
469
573
|
)
|
|
470
|
-
force_authenticate(request, user=self._make_authenticated_user(["
|
|
574
|
+
force_authenticate(request, user=self._make_authenticated_user(["User Manager"]))
|
|
471
575
|
|
|
472
576
|
response = workflow_views.AdminUserCreateView.as_view()(request)
|
|
473
577
|
|
|
@@ -483,7 +587,7 @@ class PackageAdminUserWorkflowViewsTest(SimpleTestCase):
|
|
|
483
587
|
},
|
|
484
588
|
format="json",
|
|
485
589
|
)
|
|
486
|
-
force_authenticate(request, user=self._make_authenticated_user(["
|
|
590
|
+
force_authenticate(request, user=self._make_authenticated_user(["User Manager"]))
|
|
487
591
|
|
|
488
592
|
response = workflow_views.AdminUserCreateView.as_view()(request)
|
|
489
593
|
|
|
@@ -507,7 +611,7 @@ class PackageAdminUserWorkflowViewsTest(SimpleTestCase):
|
|
|
507
611
|
},
|
|
508
612
|
format="json",
|
|
509
613
|
)
|
|
510
|
-
force_authenticate(request, user=self._make_authenticated_user(["
|
|
614
|
+
force_authenticate(request, user=self._make_authenticated_user(["User Manager"]))
|
|
511
615
|
|
|
512
616
|
response = workflow_views.AdminUserCreateView.as_view()(request)
|
|
513
617
|
|
|
@@ -530,7 +634,7 @@ class PackageAdminUserWorkflowViewsTest(SimpleTestCase):
|
|
|
530
634
|
},
|
|
531
635
|
format="json",
|
|
532
636
|
)
|
|
533
|
-
force_authenticate(request, user=self._make_authenticated_user(["
|
|
637
|
+
force_authenticate(request, user=self._make_authenticated_user(["User Manager"]))
|
|
534
638
|
|
|
535
639
|
response = workflow_views.AdminUserCreateView.as_view()(request)
|
|
536
640
|
|
|
@@ -541,8 +645,8 @@ class PackageAdminUserWorkflowViewsTest(SimpleTestCase):
|
|
|
541
645
|
unauthenticated_user = SimpleNamespace(is_authenticated=False)
|
|
542
646
|
missing_group_manager_user = SimpleNamespace(is_authenticated=True, groups=None)
|
|
543
647
|
|
|
544
|
-
self.assertFalse(workflow_views._user_has_group(unauthenticated_user, "
|
|
545
|
-
self.assertFalse(workflow_views._user_has_group(missing_group_manager_user, "
|
|
648
|
+
self.assertFalse(workflow_views._user_has_group(unauthenticated_user, "User Manager"))
|
|
649
|
+
self.assertFalse(workflow_views._user_has_group(missing_group_manager_user, "User Manager"))
|
|
546
650
|
|
|
547
651
|
def test_user_has_group_uses_filter_exists_when_available(self):
|
|
548
652
|
filtered_groups = SimpleNamespace(exists=lambda: True)
|
|
@@ -551,7 +655,7 @@ class PackageAdminUserWorkflowViewsTest(SimpleTestCase):
|
|
|
551
655
|
groups=SimpleNamespace(filter=lambda **kwargs: filtered_groups),
|
|
552
656
|
)
|
|
553
657
|
|
|
554
|
-
self.assertTrue(workflow_views._user_has_group(user, "
|
|
658
|
+
self.assertTrue(workflow_views._user_has_group(user, "User Manager"))
|
|
555
659
|
|
|
556
660
|
def test_user_has_group_returns_false_without_filter_or_all(self):
|
|
557
661
|
user = SimpleNamespace(
|
|
@@ -559,4 +663,4 @@ class PackageAdminUserWorkflowViewsTest(SimpleTestCase):
|
|
|
559
663
|
groups=SimpleNamespace(),
|
|
560
664
|
)
|
|
561
665
|
|
|
562
|
-
self.assertFalse(workflow_views._user_has_group(user, "
|
|
666
|
+
self.assertFalse(workflow_views._user_has_group(user, "User Manager"))
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
2.1.1
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/services/email.py
RENAMED
|
File without changes
|
{bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/users/__init__.py
RENAMED
|
File without changes
|
|
File without changes
|
{bmorricaldjangoworkflows-2.1.1 → bmorricaldjangoworkflows-2.1.2}/django_workflows/users/urls.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|