From a7678cad12819264a68c64ed13ac5b5da758895f Mon Sep 17 00:00:00 2001 From: richard-dds Date: Thu, 30 Aug 2018 17:00:48 -0400 Subject: [PATCH 1/9] New domain method for adding workspace user --- atst/domain/workspace_users.py | 4 ++++ atst/domain/workspaces.py | 20 ++++++++++++++++++++ atst/models/workspace_user.py | 4 ++++ tests/domain/test_workspaces.py | 32 ++++++++++++++++++++++++++++++++ 4 files changed, 60 insertions(+) diff --git a/atst/domain/workspace_users.py b/atst/domain/workspace_users.py index 940cf2a4..6fb761c3 100644 --- a/atst/domain/workspace_users.py +++ b/atst/domain/workspace_users.py @@ -33,6 +33,8 @@ class WorkspaceUsers(object): @classmethod def add(cls, user, workspace_id, role_name): role = Roles.get(role_name) + + new_workspace_role = None try: existing_workspace_role = ( db.session.query(WorkspaceRole) @@ -53,6 +55,8 @@ class WorkspaceUsers(object): db.session.add(user) db.session.commit() + return WorkspaceUser(user, new_workspace_role) + @classmethod def add_many(cls, workspace_id, workspace_user_dicts): workspace_users = [] diff --git a/atst/domain/workspaces.py b/atst/domain/workspaces.py index 6a513607..6570a831 100644 --- a/atst/domain/workspaces.py +++ b/atst/domain/workspaces.py @@ -7,6 +7,8 @@ from atst.domain.exceptions import NotFoundError, UnauthorizedError from atst.domain.roles import Roles from atst.domain.authz import Authorization from atst.models.permissions import Permissions +from atst.domain.users import Users +from atst.domain.workspace_users import WorkspaceUsers class Workspaces(object): @@ -61,6 +63,24 @@ class Workspaces(object): ) return workspaces + @classmethod + def create_member(cls, user, workspace, data): + if not Authorization.has_workspace_permission( + user, workspace, Permissions.ASSIGN_AND_UNASSIGN_ATAT_ROLE + ): + raise UnauthorizedError(user, "create workspace member") + + new_user = Users.get_or_create_by_dod_id( + data["dod_id"], + first_name=data["first_name"], + last_name=data["last_name"], + email=data["email"], + ) + workspace_user = WorkspaceUsers.add( + new_user, workspace.id, data["workspace_role"] + ) + return workspace_user + @classmethod def _create_workspace_role(cls, user, workspace, role_name): role = Roles.get(role_name) diff --git a/atst/models/workspace_user.py b/atst/models/workspace_user.py index 6faba2d6..5e3ee1ed 100644 --- a/atst/models/workspace_user.py +++ b/atst/models/workspace_user.py @@ -10,5 +10,9 @@ class WorkspaceUser(object): ) return set(workspace_permissions).union(atat_permissions) + @property + def workspace(self): + return self.workspace_role.workspace + def workspace_id(self): return self.workspace_role.workspace_id diff --git a/tests/domain/test_workspaces.py b/tests/domain/test_workspaces.py index 468536a8..4fdfa88b 100644 --- a/tests/domain/test_workspaces.py +++ b/tests/domain/test_workspaces.py @@ -87,3 +87,35 @@ def test_get_for_update_blocks_developer(): with pytest.raises(UnauthorizedError): Workspaces.get_for_update(developer, workspace.id) + + +def test_can_create_workspace_user(): + owner = UserFactory.create() + workspace = Workspaces.create(RequestFactory.create(creator=owner)) + + user_data = { + "first_name": "New", + "last_name": "User", + "email": "new.user@mail.com", + "workspace_role": "developer", + "dod_id": "1234567890" + } + + new_member = Workspaces.create_member(owner, workspace, user_data) + assert new_member.workspace == workspace + + +def test_need_permission_to_create_workspace_user(): + workspace = Workspaces.create(request=RequestFactory.create()) + random_user = UserFactory.create() + + user_data = { + "first_name": "New", + "last_name": "User", + "email": "new.user@mail.com", + "workspace_role": "developer", + "dod_id": "1234567890" + } + + with pytest.raises(UnauthorizedError): + Workspaces.create_member(random_user, workspace, user_data) From b793d4a00ae4f99f2bbdfe1429526de5bce672c3 Mon Sep 17 00:00:00 2001 From: richard-dds Date: Thu, 30 Aug 2018 17:01:11 -0400 Subject: [PATCH 2/9] New route for creating workspace user --- atst/routes/workspaces.py | 12 ++++++++++++ templates/member_new.html | 7 +++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index fcc5f7ae..de5240d5 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -101,3 +101,15 @@ def new_member(workspace_id): workspace = Workspaces.get(g.current_user, workspace_id) form = NewMemberForm() return render_template("member_new.html", workspace=workspace, form=form) + + +@bp.route("/workspaces//members/new", methods=["POST"]) +def create_member(workspace_id): + workspace = Workspaces.get(g.current_user, workspace_id) + form = NewMemberForm(http_request.form) + + if form.validate(): + Workspaces.create_member(g.current_user, workspace, form.data) + return redirect(url_for("workspaces.new_member", memberCreated=True)) + else: + return render_template("member_new.html", workspace=workspace, form=form) diff --git a/templates/member_new.html b/templates/member_new.html index a1819bed..53c49478 100644 --- a/templates/member_new.html +++ b/templates/member_new.html @@ -7,7 +7,8 @@ {% block content %} -
+ + {{ form.csrf_token }}
@@ -27,9 +28,7 @@
- - Add User - + {{ Icon('x') }} Cancel From c78aac2dead2ce5330c0893351a7d0499a726a90 Mon Sep 17 00:00:00 2001 From: richard-dds Date: Fri, 31 Aug 2018 10:05:04 -0400 Subject: [PATCH 3/9] Formatting --- tests/domain/test_workspaces.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests/domain/test_workspaces.py b/tests/domain/test_workspaces.py index 4fdfa88b..f3cca7f9 100644 --- a/tests/domain/test_workspaces.py +++ b/tests/domain/test_workspaces.py @@ -98,7 +98,7 @@ def test_can_create_workspace_user(): "last_name": "User", "email": "new.user@mail.com", "workspace_role": "developer", - "dod_id": "1234567890" + "dod_id": "1234567890", } new_member = Workspaces.create_member(owner, workspace, user_data) @@ -114,7 +114,7 @@ def test_need_permission_to_create_workspace_user(): "last_name": "User", "email": "new.user@mail.com", "workspace_role": "developer", - "dod_id": "1234567890" + "dod_id": "1234567890", } with pytest.raises(UnauthorizedError): From b1326a7c75d7b2bd964a864612c6f914c3c2e50d Mon Sep 17 00:00:00 2001 From: richard-dds Date: Fri, 31 Aug 2018 13:58:25 -0400 Subject: [PATCH 4/9] Use object for selector component --- atst/forms/data.py | 30 ++++++++++++++++++++---------- js/components/selector.js | 10 +++++++--- templates/components/selector.html | 8 ++++---- 3 files changed, 31 insertions(+), 17 deletions(-) diff --git a/atst/forms/data.py b/atst/forms/data.py index 6a41c647..5c7f9b7a 100644 --- a/atst/forms/data.py +++ b/atst/forms/data.py @@ -106,27 +106,37 @@ COMPLETION_DATE_RANGES = [ WORKSPACE_ROLES = [ ( "owner", - "Workspace Owner", - "Can add, edit, deactivate access to all projects, environments, and members. Can view budget reports. Can start and edit JEDI Cloud requests.", + { + "name": "Workspace Owner", + "description": "Can add, edit, deactivate access to all projects, environments, and members. Can view budget reports. Can start and edit JEDI Cloud requests.", + }, ), ( "admin", - "Administrator", - "Can add and edit projects, environments, members, but cannot deactivate. Cannot view budget reports or JEDI Cloud requests.", + { + "name": "Administrator", + "description": "Can add and edit projects, environments, members, but cannot deactivate. Cannot view budget reports or JEDI Cloud requests.", + }, ), ( "developer", - "Developer", - "Can view only the projects and environments they are granted access to. Can also view members associated with each environment.", + { + "name": "Developer", + "description": "Can view only the projects and environments they are granted access to. Can also view members associated with each environment.", + }, ), ( "billing_auditor", - "Billing Auditor", - "Can view only the projects and environments they are granted access to. Can also view budgets and reports associated with the workspace.", + { + "name": "Billing Auditor", + "description": "Can view only the projects and environments they are granted access to. Can also view budgets and reports associated with the workspace.", + }, ), ( "security_auditor", - "Security Auditor", - "Can view only the projects and environments they are granted access to. Can also view activity logs.", + { + "name": "Security Auditor", + "description": "Can view only the projects and environments they are granted access to. Can also view activity logs.", + }, ), ] diff --git a/js/components/selector.js b/js/components/selector.js index b6c23023..e3c94a8a 100644 --- a/js/components/selector.js +++ b/js/components/selector.js @@ -26,17 +26,21 @@ export default { computed: { label: function () { - return this.value - ? this.choices.find((choice) => { + if (this.value) { + const selectedChoice = this.choices.find((choice) => { return this.value === choice[0] })[1] - : this.defaultLabel + return selectedChoice.name + } else { + return this.defaultLabel + } } }, methods: { change: function (e) { this.value = e.target.value + console.log(this.value) this.showError = false setTimeout(() => this.$refs.popover.hide(), 300) } diff --git a/templates/components/selector.html b/templates/components/selector.html index cfa44d0a..cb374286 100644 --- a/templates/components/selector.html +++ b/templates/components/selector.html @@ -44,13 +44,13 @@ v-bind:checked='value === choice[0]' v-on:change='change'/>

Add environment access.

+ {%- endset %} + + {{ Alert('Member added successfully', + message=message, + level='success' + ) }} +{% endif %} +
From 0f2ba6efd0132b54d32791fce7f3115c250b5751 Mon Sep 17 00:00:00 2001 From: richard-dds Date: Fri, 31 Aug 2018 16:05:37 -0400 Subject: [PATCH 9/9] Formatting --- atst/routes/workspaces.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index 4539d2bc..665d266c 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -114,7 +114,7 @@ def create_member(workspace_id): url_for( "workspaces.workspace_members", workspace_id=workspace.id, - newMemberName=new_member.user.full_name + newMemberName=new_member.user.full_name, ) ) else: