From 5b973ab1e49e1dfb1feebcd3f6c4408376804caf Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 10 Sep 2018 12:26:13 -0400 Subject: [PATCH 01/34] Fix selection on modal, fix a typo --- atst/models/environment_role.py | 2 +- js/components/forms/edit_workspace_member.js | 40 ++++++ templates/workspaces/members/edit.html | 138 +++++++++---------- 3 files changed, 106 insertions(+), 74 deletions(-) create mode 100644 js/components/forms/edit_workspace_member.js diff --git a/atst/models/environment_role.py b/atst/models/environment_role.py index 05357759..594b46f3 100644 --- a/atst/models/environment_role.py +++ b/atst/models/environment_role.py @@ -7,7 +7,7 @@ from atst.models import Base, types, mixins class CSPRole(Enum): - NONSENSE_ROLE = "nonesense_role" + NONSENSE_ROLE = "nonsense_role" class EnvironmentRole(Base, mixins.TimestampsMixin): diff --git a/js/components/forms/edit_workspace_member.js b/js/components/forms/edit_workspace_member.js new file mode 100644 index 00000000..f483656d --- /dev/null +++ b/js/components/forms/edit_workspace_member.js @@ -0,0 +1,40 @@ +import FormMixin from '../../mixins/form' +import textinput from '../text_input' +import Selector from '../selector' +import Modal from '../../mixins/modal' +import toggler from '../toggler' + +export default { + name: 'edit-workspace-member', + + mixins: [FormMixin, Modal], + + components: { + toggler, + Modal, + Selector, + textinput + }, + + props: { + choices: Array, + initialData: String + }, + + data: function () { + return { value: this.initialData } + }, + + methods: { + change: function (e) { + this.value = e.target.value + }, + readableName: function (role) { + return role.replace(/[_]/g, " ") + }, + }, + + mounted: function () { + console.log(this.initialData, this.choices) + } +} diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index b12538fa..24489e6f 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -52,88 +52,80 @@ {% call Modal(name='rolesModal', dismissable=False) %}
-
-

- Environment access for Danny Knight -
Project Name - Environment Name
-

-
-

An environment role determines the permissions a member of the workspace assumes when using the JEDI Cloud.

-

A member may have different environment roles across different projects. A member can only have one assigned environment role in a given environment.

-
-
+

+ Environment access for {{ member.user.full_name }} +
Project Name - Environment Name
+

-
-
    -
  • - - -
  • +
      +
    • + + +
    • -
    • - - -
    • +
    • + + +
    • -
    • - - -
    • +
    • + + +
    • -
    • - - -
    • +
    • + + +
    • -
    • - - -
    • +
    • + + +
    • -
    • - - -
    • -
    +
  • + + +
  • +
-
From 04672b9aebf3692fc50a5f7ebc058e6d64d47ff5 Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 10 Sep 2018 16:11:39 -0400 Subject: [PATCH 02/34] Catch environment role selection --- atst/forms/data.py | 17 +++++++++++++++++ atst/forms/edit_member.py | 6 +++++- atst/routes/workspaces.py | 1 + js/index.js | 4 +++- 4 files changed, 26 insertions(+), 2 deletions(-) diff --git a/atst/forms/data.py b/atst/forms/data.py index ebdeacbb..959dec96 100644 --- a/atst/forms/data.py +++ b/atst/forms/data.py @@ -186,6 +186,23 @@ ENVIRONMENT_ROLES = [ ), ] +ENVIRONMENT_ROLES = [ + ( + "developer", + { + "name": "Developer", + "description": "Configures cloud-based IaaS and PaaS computing, networking, and storage services.", + }, + ), + ( + "owner", + { + "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.", + }, + ), +] + FUNDING_TYPES = [ ("", "- Select -"), ("RDTE", "Research, Development, Testing & Evaluation (RDT&E)"), diff --git a/atst/forms/edit_member.py b/atst/forms/edit_member.py index e76df6d0..3fa3ed3c 100644 --- a/atst/forms/edit_member.py +++ b/atst/forms/edit_member.py @@ -3,7 +3,7 @@ from wtforms.validators import Optional from atst.forms.fields import SelectField -from .data import WORKSPACE_ROLES +from .data import WORKSPACE_ROLES, ENVIRONMENT_ROLES class EditMemberForm(Form): @@ -11,3 +11,7 @@ class EditMemberForm(Form): workspace_role = SelectField( "Workspace Role", choices=WORKSPACE_ROLES, validators=[Optional()] ) + + environment_role = SelectField( + "Environment Role", choices=ENVIRONMENT_ROLES, validators=[Optional()] + ) diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index d4e79fe9..23e05740 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -14,6 +14,7 @@ from atst.domain.projects import Projects from atst.domain.reports import Reports from atst.domain.workspaces import Workspaces from atst.domain.workspace_users import WorkspaceUsers +from atst.domain.environments import Environments from atst.forms.new_project import NewProjectForm from atst.forms.new_member import NewMemberForm from atst.forms.edit_member import EditMemberForm diff --git a/js/index.js b/js/index.js index 653038f4..204f9871 100644 --- a/js/index.js +++ b/js/index.js @@ -13,6 +13,7 @@ import poc from './components/forms/poc' import financial from './components/forms/financial' import toggler from './components/toggler' import NewProject from './components/forms/new_project' +import EditWorkspaceMember from './components/forms/edit_workspace_member' import Modal from './mixins/modal' import selector from './components/selector' import BudgetChart from './components/charts/budget_chart' @@ -39,7 +40,8 @@ const app = new Vue({ BudgetChart, SpendTable, CcpoApproval, - LocalDatetime + LocalDatetime, + EditWorkspaceMember, }, mounted: function() { From 19a8078bb2c2cd92ad01bd2384ea22a5be277df4 Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 10 Sep 2018 16:11:49 -0400 Subject: [PATCH 03/34] working commit --- atst/forms/data.py | 9 +- atst/routes/workspaces.py | 2 +- templates/workspaces/members/edit.html | 368 +++++++++++-------------- 3 files changed, 171 insertions(+), 208 deletions(-) diff --git a/atst/forms/data.py b/atst/forms/data.py index 959dec96..21427660 100644 --- a/atst/forms/data.py +++ b/atst/forms/data.py @@ -187,17 +187,18 @@ ENVIRONMENT_ROLES = [ ] ENVIRONMENT_ROLES = [ + (None, {"name": "No access", "description": "No environment access."}), ( - "developer", + "meow", { - "name": "Developer", + "name": "Meow", "description": "Configures cloud-based IaaS and PaaS computing, networking, and storage services.", }, ), ( - "owner", + "woof", { - "name": "Workspace Owner", + "name": "Woof", "description": "Can add, edit, deactivate access to all projects, environments, and members. Can view budget reports. Can start and edit JEDI Cloud requests.", }, ), diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index 23e05740..0dca879b 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -214,7 +214,7 @@ def view_member(workspace_id, member_id): "edit this workspace user", ) member = WorkspaceUsers.get(workspace_id, member_id) - form = EditMemberForm(workspace_role=member.role) + form = EditMemberForm(workspace_role=member.role, environment_role="") return render_template( "workspaces/members/edit.html", form=form, workspace=workspace, member=member ) diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index 24489e6f..2afcf936 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -3,223 +3,185 @@ {% from "components/icon.html" import Icon %} {% from "components/modal.html" import Modal %} {% from "components/selector.html" import Selector %} +{% from "components/options_input.html" import OptionsInput %} {% block content %} -
- {{ form.csrf_token }} + + + {{ form.csrf_token }} -
-
-

{{ member.user.full_name }}

+
+
+

{{ member.user.full_name }}

-
- {{ Selector(form.workspace_role) }} -
+
+ {{ Selector(form.workspace_role) }} +
-
-
-
-
-
DOD ID:
-
{{ member.user.dod_id }}
-
-
-
Email:
-
{{ member.user.email }}
-
-
- edit account details -
-
- -
-
-

Manage Access
Grant access to an environment

-
-
- - - - {% call Modal(name='rolesModal', dismissable=False) %} -
-
-

- Environment access for {{ member.user.full_name }} -
Project Name - Environment Name
-

-
- -
    -
  • - - -
  • - -
  • - - -
  • - -
  • - - -
  • - -
  • - - -
  • - -
  • - - -
  • - -
  • - - -
  • -
- - -
+
+
+

Manage Access
Grant access to an environment

+
+
- {% endcall %} - -
- -
+
+
+ {{ Selector(form.environment_role) }} -
- -
+ {% call Modal(name='rolesModal', dismissable=False) %} +
+
    + {% for choice in form.environment_role.choices %} +
  • -
    - - - {{ Icon('x') }} - Cancel - -
    + {% if choice[0] != "" %} + + + {% endif %} +
  • + {% endfor %} +
+ +
+ {% endcall %} - +
+ +
+ +
+ +
+ +
+ + + {{ Icon('x') }} + Cancel + +
+ + +
From 50f9794c348ecdff98dcfa66cea90357df602741 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 11 Sep 2018 09:36:46 -0400 Subject: [PATCH 04/34] Update ENVIRONMENT_ROLES --- atst/forms/data.py | 33 +++++++++++++++++++++++++++------ 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/atst/forms/data.py b/atst/forms/data.py index 21427660..b2029d88 100644 --- a/atst/forms/data.py +++ b/atst/forms/data.py @@ -189,17 +189,38 @@ ENVIRONMENT_ROLES = [ ENVIRONMENT_ROLES = [ (None, {"name": "No access", "description": "No environment access."}), ( - "meow", + "database_admin", { - "name": "Meow", - "description": "Configures cloud-based IaaS and PaaS computing, networking, and storage services.", + "name": "Database Administrator", + "description": "Configures cloud-based database services.", }, ), ( - "woof", + "devops", { - "name": "Woof", - "description": "Can add, edit, deactivate access to all projects, environments, and members. Can view budget reports. Can start and edit JEDI Cloud requests.", + "name": "DevOps", + "description": "Provisions, deprovisions, and deploys cloud-based IaaS and PaaS computing, networking, and storage services, including pre-configured machine images.", + }, + ), + ( + "billing_admin", + { + "name": "Billing Administrator", + "description": "Views cloud resource usage, budget reports, and invoices; Tracks budgets, including spend reports, cost planning and projections, and sets limits based on cloud service usage.", + }, + ), + ( + "security_admin", + { + "name": "Security Administrator", + "description": "Accesses information security and control tools of cloud resources which include viewing cloud resource usage logging, user roles and permissioning history.", + }, + ), + ( + "financial_auditor", + { + "name": "Financial Auditor", + "description": "Views cloud resource usage and budget reports.", }, ), ] From 3c9243686a7802df8d30213c0b24cccb3631a2d2 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 11 Sep 2018 13:46:46 -0400 Subject: [PATCH 05/34] Don't use Vue for the environment roles modal --- templates/workspaces/members/edit.html | 57 ++++++++++++-------------- 1 file changed, 27 insertions(+), 30 deletions(-) diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index 2afcf936..dc592b9c 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -50,39 +50,36 @@
- {{ Selector(form.environment_role) }} + {% call Modal(name='rolesModal', dismissable=False) %} -
- +
- {% endcall %}
From 5a5d81eb7fabb35ffe36127ca59834f246c29971 Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 13 Sep 2018 15:20:34 -0400 Subject: [PATCH 08/34] Update the template to show real data --- atst/domain/environments.py | 9 ++-- atst/domain/projects.py | 18 +++++++ atst/routes/workspaces.py | 7 ++- templates/workspaces/members/edit.html | 74 ++++++-------------------- 4 files changed, 43 insertions(+), 65 deletions(-) diff --git a/atst/domain/environments.py b/atst/domain/environments.py index 66910c6e..666bb8bd 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -4,7 +4,6 @@ from atst.database import db from atst.models.environment import Environment from atst.models.environment_role import EnvironmentRole, CSPRole from atst.models.project import Project -from atst.domain.users import Users from .exceptions import NotFoundError @@ -59,13 +58,13 @@ class Environments(object): new_role = environment_data["user_role_name"] environment = Environments.get(cls=cls, environment_id=environment_data["id"]) if workspace_user.has_environment_roles: - env_role = EnvironmentRole.get(workspace_user.user_id, environment.id) + env_role = EnvironmentRole.get( + workspace_user.user_id, environment_data["id"] + ) env_role.role = new_role else: env_role = EnvironmentRole( - user=workspace_user.user, - environment=environment, - role=new_role + user=workspace_user.user, environment=environment, role=new_role ) db.session.add(env_role) db.session.commit() diff --git a/atst/domain/projects.py b/atst/domain/projects.py index 4d03f80f..94ad6a39 100644 --- a/atst/domain/projects.py +++ b/atst/domain/projects.py @@ -49,3 +49,21 @@ class Projects(object): .filter(EnvironmentRole.user_id == user.id) .all() ) + + @classmethod + def get_all(cls, workspace_user, workspace): + Authorization.check_workspace_permission( + workspace_user.user, + workspace, + Permissions.VIEW_APPLICATION_IN_WORKSPACE, + "view project in workspace", + ) + + try: + projects = ( + db.session.query(Project).filter_by(workspace_id=workspace.id).all() + ) + except NoResultFound: + raise NotFoundError("projects") + + return projects diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index ef156dc8..b3ac5b1b 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -214,9 +214,14 @@ def view_member(workspace_id, member_id): "edit this workspace user", ) member = WorkspaceUsers.get(workspace_id, member_id) + projects = Projects.get_all(member, workspace) form = EditMemberForm(workspace_role=member.role, environment_role="") return render_template( - "workspaces/members/edit.html", form=form, workspace=workspace, member=member + "workspaces/members/edit.html", + workspace=workspace, + member=member, + projects=projects, + form=form, ) diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index ffdd99c2..c21471e3 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -88,83 +88,39 @@
{% endcall %} + {% for project in projects %}
-
- -
-
+ {% endfor %}
From 0d80604fcd15141b65d5c861de4127d5e652d83e Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 17 Sep 2018 13:41:35 -0400 Subject: [PATCH 11/34] Fix bug --- atst/domain/environments.py | 9 ++++----- js/components/forms/edit_workspace_member.js | 9 +-------- 2 files changed, 5 insertions(+), 13 deletions(-) diff --git a/atst/domain/environments.py b/atst/domain/environments.py index 666bb8bd..184eea42 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -44,6 +44,7 @@ class Environments(object): .all() ) + @classmethod def get(cls, environment_id): try: env = db.session.query(Environment).filter_by(id=environment_id).one() @@ -56,11 +57,9 @@ class Environments(object): def update_environment_role(cls, environment_data, workspace_user): # TODO need to check permissions? new_role = environment_data["user_role_name"] - environment = Environments.get(cls=cls, environment_id=environment_data["id"]) - if workspace_user.has_environment_roles: - env_role = EnvironmentRole.get( - workspace_user.user_id, environment_data["id"] - ) + environment = Environments.get(environment_data["id"]) + env_role = EnvironmentRole.get(member.user_id, environment_data["id"]) + if env_role: env_role.role = new_role else: env_role = EnvironmentRole( diff --git a/js/components/forms/edit_workspace_member.js b/js/components/forms/edit_workspace_member.js index 1c31fb69..73a87c62 100644 --- a/js/components/forms/edit_workspace_member.js +++ b/js/components/forms/edit_workspace_member.js @@ -28,13 +28,6 @@ export default { methods: { change: function (e) { this.value = e.target.value - }, - readableName: function (role) { - return role.replace(/[_]/g, " ") - }, - }, - - mounted: function () { - console.log(this.initialData, this.choices) + } } } From ccce20c48c2c80226f5869d916268a7b2880c2f9 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 18 Sep 2018 08:27:41 -0400 Subject: [PATCH 12/34] Update environment role with real data --- atst/domain/environments.py | 23 +++++++++++++---------- atst/routes/workspaces.py | 18 ++++++++++-------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/atst/domain/environments.py b/atst/domain/environments.py index 184eea42..b60cc454 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -56,14 +56,17 @@ class Environments(object): @classmethod def update_environment_role(cls, environment_data, workspace_user): # TODO need to check permissions? - new_role = environment_data["user_role_name"] - environment = Environments.get(environment_data["id"]) - env_role = EnvironmentRole.get(member.user_id, environment_data["id"]) - if env_role: - env_role.role = new_role - else: - env_role = EnvironmentRole( - user=workspace_user.user, environment=environment, role=new_role + for i in range(len(environment_data)): + new_role = environment_data[i]["role"] + environment = Environments.get(environment_data[i]["id"]) + env_role = EnvironmentRole.get( + workspace_user.user_id, environment_data[i]["id"] ) - db.session.add(env_role) - db.session.commit() + if env_role: + env_role.role = new_role + else: + env_role = EnvironmentRole( + user=workspace_user.user, environment=environment, role=new_role + ) + db.session.add(env_role) + db.session.commit() diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index 0c42c15e..f27189e8 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -1,3 +1,4 @@ +import re, ast from datetime import date, timedelta from flask import ( @@ -239,6 +240,13 @@ def update_member(workspace_id, member_id): "edit this workspace user", ) member = WorkspaceUsers.get(workspace_id, member_id) + + environment_data = [] + form_dict = http_request.form.to_dict() + for entry in form_dict: + if re.match("env_", entry): + environment_data.append(ast.literal_eval(form_dict[entry])) + form = EditMemberForm(http_request.form) if form.validate(): @@ -248,14 +256,8 @@ def update_member(workspace_id, member_id): g.current_user, workspace, member, form.data["workspace_role"] ) new_role_name = member.role_displayname - if form.data["environment_role"]: - new_env_role = form.data["environment_role"] - environment_data = { - "id": "9432c6a5-2f9d-4c9c-b553-4c175852fb65", - "name": "this environment", - "user_role_name": new_env_role, - } - Environments.update_environment_role(environment_data, member) + + Environments.update_environment_role(environment_data, member) return redirect( url_for( From 32d04bfce45ba59a0355fbe180da6b3df32d6762 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 18 Sep 2018 10:56:56 -0400 Subject: [PATCH 13/34] Make environment role display dynamic --- atst/forms/data.py | 8 +- js/components/forms/edit_workspace_member.js | 10 +- templates/workspaces/members/edit.html | 224 ++++++++++--------- 3 files changed, 130 insertions(+), 112 deletions(-) diff --git a/atst/forms/data.py b/atst/forms/data.py index b2029d88..8c42bf11 100644 --- a/atst/forms/data.py +++ b/atst/forms/data.py @@ -187,7 +187,13 @@ ENVIRONMENT_ROLES = [ ] ENVIRONMENT_ROLES = [ - (None, {"name": "No access", "description": "No environment access."}), + ( + "no_access", + { + "name": "no access", + "description": "No environment access." + } + ), ( "database_admin", { diff --git a/js/components/forms/edit_workspace_member.js b/js/components/forms/edit_workspace_member.js index 73a87c62..08ea5d14 100644 --- a/js/components/forms/edit_workspace_member.js +++ b/js/components/forms/edit_workspace_member.js @@ -18,7 +18,8 @@ export default { props: { choices: Array, - initialData: Object + initialData: String + }, data: function () { @@ -28,6 +29,11 @@ export default { methods: { change: function (e) { this.value = e.target.value - } + }, + // method to map ugly name to human readable name here + }, + + mounted: function () { + console.log(this.initialData, this.choices) } } diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index 71cfe8b4..c1736eb8 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -7,137 +7,143 @@ {% block content %} - -
- {{ form.csrf_token }} + + {{ form.csrf_token }} -
-
-

{{ member.user.full_name }}

+
+
+

{{ member.user.full_name }}

-
- {{ Selector(form.workspace_role) }} -
+
+ {{ Selector(form.workspace_role) }} +
-
-
-
-
-
DOD ID:
-
{{ member.user.dod_id }}
-
-
-
Email:
-
{{ member.user.email }}
-
-
- edit account details -
- -
-
-

Manage Access
Grant access to an environment

-
+
+
+
+
DOD ID:
+
{{ member.user.dod_id }}
+
+
+
Email:
+
{{ member.user.email }}
+
+
+ edit account details
+
- + + {% for project in projects %} +
+ +
+ {% endfor %} -
- - - {{ Icon('x') }} - Cancel - -
+
+ + + {{ Icon('x') }} + Cancel + +
- From bead1990c9c74d3f81ce6367f4ce9474eca92395 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 18 Sep 2018 11:24:24 -0400 Subject: [PATCH 14/34] Let input value be simple --- atst/domain/environments.py | 10 +++++----- atst/forms/data.py | 8 +------- atst/routes/workspaces.py | 10 ++++++---- templates/workspaces/members/edit.html | 6 +++--- 4 files changed, 15 insertions(+), 19 deletions(-) diff --git a/atst/domain/environments.py b/atst/domain/environments.py index b60cc454..7a49c49c 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -54,13 +54,13 @@ class Environments(object): return env @classmethod - def update_environment_role(cls, environment_data, workspace_user): + def update_environment_role(cls, ids_and_roles, workspace_user): # TODO need to check permissions? - for i in range(len(environment_data)): - new_role = environment_data[i]["role"] - environment = Environments.get(environment_data[i]["id"]) + for i in range(len(ids_and_roles)): + new_role = ids_and_roles[i]["role"] + environment = Environments.get(ids_and_roles[i]["id"]) env_role = EnvironmentRole.get( - workspace_user.user_id, environment_data[i]["id"] + workspace_user.user_id, ids_and_roles[i]["id"] ) if env_role: env_role.role = new_role diff --git a/atst/forms/data.py b/atst/forms/data.py index 8c42bf11..7be3e2ad 100644 --- a/atst/forms/data.py +++ b/atst/forms/data.py @@ -187,13 +187,7 @@ ENVIRONMENT_ROLES = [ ] ENVIRONMENT_ROLES = [ - ( - "no_access", - { - "name": "no access", - "description": "No environment access." - } - ), + ("no_access", {"name": "no access", "description": "No environment access."}), ( "database_admin", { diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index f27189e8..f9a52957 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -1,4 +1,4 @@ -import re, ast +import re from datetime import date, timedelta from flask import ( @@ -241,11 +241,13 @@ def update_member(workspace_id, member_id): ) member = WorkspaceUsers.get(workspace_id, member_id) - environment_data = [] + ids_and_roles = [] form_dict = http_request.form.to_dict() for entry in form_dict: if re.match("env_", entry): - environment_data.append(ast.literal_eval(form_dict[entry])) + env_id = entry[4:] + env_role = form_dict[entry] + ids_and_roles.append({"id": env_id, "role": env_role}) form = EditMemberForm(http_request.form) @@ -257,7 +259,7 @@ def update_member(workspace_id, member_id): ) new_role_name = member.role_displayname - Environments.update_environment_role(environment_data, member) + Environments.update_environment_role(ids_and_roles, member) return redirect( url_for( diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index c1736eb8..a8f767e3 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -91,11 +91,11 @@ {% if choice[0] != "" %} Date: Tue, 18 Sep 2018 14:05:07 -0400 Subject: [PATCH 15/34] Vue function to display the environment role in a friendlier format --- js/components/forms/edit_workspace_member.js | 4 +++- templates/workspaces/members/edit.html | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/js/components/forms/edit_workspace_member.js b/js/components/forms/edit_workspace_member.js index 08ea5d14..7b43599e 100644 --- a/js/components/forms/edit_workspace_member.js +++ b/js/components/forms/edit_workspace_member.js @@ -30,7 +30,9 @@ export default { change: function (e) { this.value = e.target.value }, - // method to map ugly name to human readable name here + readableName: function (role) { + return role.replace(/[_]/g, " ") + }, }, mounted: function () { diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index a8f767e3..4506ce61 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -81,7 +81,7 @@
{{ form.data["environment_role"] }}
- + {% call Modal(name=env.name + 'RolesModal', dismissable=False) %}
From b973d3cf458f6efd131870bda4f9f99fdc15fcb0 Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 20 Sep 2018 09:31:47 -0400 Subject: [PATCH 16/34] Clear the changed environment display if the user cancels out of modal --- js/components/forms/edit_workspace_member.js | 16 +++++++++------- templates/workspaces/members/edit.html | 2 +- 2 files changed, 10 insertions(+), 8 deletions(-) diff --git a/js/components/forms/edit_workspace_member.js b/js/components/forms/edit_workspace_member.js index 7b43599e..718b28bf 100644 --- a/js/components/forms/edit_workspace_member.js +++ b/js/components/forms/edit_workspace_member.js @@ -18,12 +18,13 @@ export default { props: { choices: Array, - initialData: String - + initialData: String, }, data: function () { - return { value: this.initialData } + return { + value: this.initialData, + } }, methods: { @@ -33,9 +34,10 @@ export default { readableName: function (role) { return role.replace(/[_]/g, " ") }, + cancel: function (current_role, selected_role) { + if (current_role != selected_role) { + this.value = current_role + } + } }, - - mounted: function () { - console.log(this.initialData, this.choices) - } } diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index 4506ce61..7060b8b3 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -117,7 +117,7 @@
From a1cf4335cef491474f78ae720f45835ff81f747b Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 20 Sep 2018 09:45:49 -0400 Subject: [PATCH 17/34] Use Vue to get label class dynamically --- js/components/forms/edit_workspace_member.js | 6 +++++- templates/workspaces/members/edit.html | 7 +------ 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/js/components/forms/edit_workspace_member.js b/js/components/forms/edit_workspace_member.js index 718b28bf..be3ebc51 100644 --- a/js/components/forms/edit_workspace_member.js +++ b/js/components/forms/edit_workspace_member.js @@ -24,6 +24,7 @@ export default { data: function () { return { value: this.initialData, + label_class: this.initialData, } }, @@ -31,7 +32,10 @@ export default { change: function (e) { this.value = e.target.value }, - readableName: function (role) { + displayName: function (role) { + this.label_class = role === "no_access" ? + "label" : "label label--success" + return role.replace(/[_]/g, " ") }, cancel: function (current_role, selected_role) { diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index 7060b8b3..78642519 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -65,11 +65,6 @@ {% for env in project.environments %} {% set role = EnvironmentRole.get(member.user_id, env.id).role or 'no_access' %} - {% set label_class = 'label' %} - {% if role != 'no_access' %} - {% set label_class = 'label label--success' %} - {% endif %} -
  • @@ -81,7 +76,7 @@
    {{ form.data["environment_role"] }}
    - + {% call Modal(name=env.name + 'RolesModal', dismissable=False) %}
    From 43a91e8c22568f44b279647eb4ada61075dba879 Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 20 Sep 2018 10:53:57 -0400 Subject: [PATCH 18/34] Display the environment role nicely --- js/components/forms/edit_workspace_member.js | 22 +++++++++++++------- templates/workspaces/members/edit.html | 2 +- 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/js/components/forms/edit_workspace_member.js b/js/components/forms/edit_workspace_member.js index be3ebc51..00d60a2c 100644 --- a/js/components/forms/edit_workspace_member.js +++ b/js/components/forms/edit_workspace_member.js @@ -24,7 +24,6 @@ export default { data: function () { return { value: this.initialData, - label_class: this.initialData, } }, @@ -32,16 +31,25 @@ export default { change: function (e) { this.value = e.target.value }, - displayName: function (role) { - this.label_class = role === "no_access" ? - "label" : "label label--success" - - return role.replace(/[_]/g, " ") - }, cancel: function (current_role, selected_role) { if (current_role != selected_role) { this.value = current_role } } }, + + computed: { + displayName: function () { + for (var arr in this.choices) { + if (this.choices[arr][0] == this.value) { + return this.choices[arr][1].name + } + } + return this.value + }, + label_class: function () { + return this.value === "no_access" ? + "label" : "label label--success" + } + } } diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index 78642519..774541cd 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -76,7 +76,7 @@
    {{ form.data["environment_role"] }}
    - + {% call Modal(name=env.name + 'RolesModal', dismissable=False) %}
    From 95d826be09d5902ea4a214ee8c859af771c5d812 Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 20 Sep 2018 11:04:31 -0400 Subject: [PATCH 19/34] All modals use activeModal --- js/mixins/modal.js | 6 ------ 1 file changed, 6 deletions(-) diff --git a/js/mixins/modal.js b/js/mixins/modal.js index 8beb5426..df168c59 100644 --- a/js/mixins/modal.js +++ b/js/mixins/modal.js @@ -11,12 +11,6 @@ export default { }, data: function() { return { - modals: { - styleguidemodal: false, - newprojectconfirmation: false, - pendingfinancialverification: false, - pendingccpoapproval: false, - }, activeModal: null, } } From 5522811f4ed864e61776f34fb8a373382d702187 Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 20 Sep 2018 11:22:59 -0400 Subject: [PATCH 20/34] Add permission check and refactor update_environment_role method --- atst/domain/environments.py | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/atst/domain/environments.py b/atst/domain/environments.py index 7a49c49c..5a079f4e 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -4,6 +4,8 @@ from atst.database import db from atst.models.environment import Environment from atst.models.environment_role import EnvironmentRole, CSPRole from atst.models.project import Project +from atst.models.permissions import Permissions +from atst.domain.authz import Authorization from .exceptions import NotFoundError @@ -55,13 +57,17 @@ class Environments(object): @classmethod def update_environment_role(cls, ids_and_roles, workspace_user): - # TODO need to check permissions? - for i in range(len(ids_and_roles)): - new_role = ids_and_roles[i]["role"] - environment = Environments.get(ids_and_roles[i]["id"]) - env_role = EnvironmentRole.get( - workspace_user.user_id, ids_and_roles[i]["id"] - ) + Authorization.check_workspace_permission( + user, + workspace, + Permissions.ADD_AND_ASSIGN_CSP_ROLES, + "assign environment roles", + ) + + for id_and_role in ids_and_roles: + new_role = id_and_role["role"] + environment = Environments.get(id_and_role["id"]) + env_role = EnvironmentRole.get(workspace_user.user_id, id_and_role["id"]) if env_role: env_role.role = new_role else: @@ -69,4 +75,5 @@ class Environments(object): user=workspace_user.user, environment=environment, role=new_role ) db.session.add(env_role) - db.session.commit() + + db.session.commit() From e9f9ff0cd5943a3c889863d7b22736c0bf72c003 Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 20 Sep 2018 14:41:07 -0400 Subject: [PATCH 21/34] Fix update environment role bug --- atst/domain/environments.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/atst/domain/environments.py b/atst/domain/environments.py index 5a079f4e..ce4da176 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -58,8 +58,8 @@ class Environments(object): @classmethod def update_environment_role(cls, ids_and_roles, workspace_user): Authorization.check_workspace_permission( - user, - workspace, + workspace_user.user, + workspace_user.workspace, Permissions.ADD_AND_ASSIGN_CSP_ROLES, "assign environment roles", ) From a8b777acda5ded65abf798d8ae89f91f2b36e80f Mon Sep 17 00:00:00 2001 From: Montana Date: Thu, 20 Sep 2018 15:04:33 -0400 Subject: [PATCH 22/34] Infer if the user has no environment role --- atst/forms/data.py | 39 -------------------- js/components/forms/edit_workspace_member.js | 4 +- templates/workspaces/members/edit.html | 2 +- 3 files changed, 3 insertions(+), 42 deletions(-) diff --git a/atst/forms/data.py b/atst/forms/data.py index 7be3e2ad..ebdeacbb 100644 --- a/atst/forms/data.py +++ b/atst/forms/data.py @@ -186,45 +186,6 @@ ENVIRONMENT_ROLES = [ ), ] -ENVIRONMENT_ROLES = [ - ("no_access", {"name": "no access", "description": "No environment access."}), - ( - "database_admin", - { - "name": "Database Administrator", - "description": "Configures cloud-based database services.", - }, - ), - ( - "devops", - { - "name": "DevOps", - "description": "Provisions, deprovisions, and deploys cloud-based IaaS and PaaS computing, networking, and storage services, including pre-configured machine images.", - }, - ), - ( - "billing_admin", - { - "name": "Billing Administrator", - "description": "Views cloud resource usage, budget reports, and invoices; Tracks budgets, including spend reports, cost planning and projections, and sets limits based on cloud service usage.", - }, - ), - ( - "security_admin", - { - "name": "Security Administrator", - "description": "Accesses information security and control tools of cloud resources which include viewing cloud resource usage logging, user roles and permissioning history.", - }, - ), - ( - "financial_auditor", - { - "name": "Financial Auditor", - "description": "Views cloud resource usage and budget reports.", - }, - ), -] - FUNDING_TYPES = [ ("", "- Select -"), ("RDTE", "Research, Development, Testing & Evaluation (RDT&E)"), diff --git a/js/components/forms/edit_workspace_member.js b/js/components/forms/edit_workspace_member.js index 00d60a2c..4785217c 100644 --- a/js/components/forms/edit_workspace_member.js +++ b/js/components/forms/edit_workspace_member.js @@ -45,10 +45,10 @@ export default { return this.choices[arr][1].name } } - return this.value + return this.value ? this.value : "no access" }, label_class: function () { - return this.value === "no_access" ? + return this.displayName === "no access" ? "label" : "label label--success" } } diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index 774541cd..44eac364 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -64,7 +64,7 @@
      {% for env in project.environments %} - {% set role = EnvironmentRole.get(member.user_id, env.id).role or 'no_access' %} + {% set role = EnvironmentRole.get(member.user_id, env.id).role %}
    • From a071ff625fcc87182603983ede88358ba2887819 Mon Sep 17 00:00:00 2001 From: Montana Date: Fri, 21 Sep 2018 09:33:49 -0400 Subject: [PATCH 23/34] Rename confusing variable --- js/components/forms/edit_workspace_member.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/js/components/forms/edit_workspace_member.js b/js/components/forms/edit_workspace_member.js index 4785217c..37cf00bb 100644 --- a/js/components/forms/edit_workspace_member.js +++ b/js/components/forms/edit_workspace_member.js @@ -23,29 +23,30 @@ export default { data: function () { return { - value: this.initialData, + new_role: this.initialData, } }, methods: { change: function (e) { - this.value = e.target.value + e.preventDefault() + this.new_role = e.target.value }, cancel: function (current_role, selected_role) { if (current_role != selected_role) { - this.value = current_role + this.new_role = current_role } - } + }, }, computed: { displayName: function () { for (var arr in this.choices) { - if (this.choices[arr][0] == this.value) { + if (this.choices[arr][0] == this.new_role) { return this.choices[arr][1].name } } - return this.value ? this.value : "no access" + return this.new_role ? this.new_role : "no access" }, label_class: function () { return this.displayName === "no access" ? From 6d50c228afa0193ab47729cb9298d73efc96ae55 Mon Sep 17 00:00:00 2001 From: Montana Date: Fri, 21 Sep 2018 10:55:41 -0400 Subject: [PATCH 24/34] Fix bug that would save a user's modal selection even if the user canceled out of the modal --- js/components/forms/edit_workspace_member.js | 8 +++----- templates/workspaces/members/edit.html | 5 +++-- 2 files changed, 6 insertions(+), 7 deletions(-) diff --git a/js/components/forms/edit_workspace_member.js b/js/components/forms/edit_workspace_member.js index 37cf00bb..2a5c5e2a 100644 --- a/js/components/forms/edit_workspace_member.js +++ b/js/components/forms/edit_workspace_member.js @@ -32,10 +32,8 @@ export default { e.preventDefault() this.new_role = e.target.value }, - cancel: function (current_role, selected_role) { - if (current_role != selected_role) { - this.new_role = current_role - } + cancel: function () { + this.new_role = this.initialData }, }, @@ -51,6 +49,6 @@ export default { label_class: function () { return this.displayName === "no access" ? "label" : "label label--success" - } + }, } } diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index 44eac364..31252b1e 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -86,7 +86,7 @@ {% if choice[0] != "" %} {% endfor %}
    +
    From e12c1f42db08133561917cc1414e5233b6aa09ff Mon Sep 17 00:00:00 2001 From: Montana Date: Fri, 21 Sep 2018 11:14:30 -0400 Subject: [PATCH 25/34] Move environment role method into domain --- atst/domain/environment_roles.py | 16 ++++++++++++++++ atst/domain/environments.py | 3 ++- atst/routes/workspaces.py | 4 ++-- templates/workspaces/members/edit.html | 2 +- 4 files changed, 21 insertions(+), 4 deletions(-) create mode 100644 atst/domain/environment_roles.py diff --git a/atst/domain/environment_roles.py b/atst/domain/environment_roles.py new file mode 100644 index 00000000..8b10a2db --- /dev/null +++ b/atst/domain/environment_roles.py @@ -0,0 +1,16 @@ +from atst.models.environment_role import EnvironmentRole +from atst.database import db + + +class EnvironmentRoles(object): + @classmethod + def get(cls, user_id, environment_id): + existing_env_role = ( + db.session.query(EnvironmentRole) + .filter( + EnvironmentRole.user_id == user_id, + EnvironmentRole.environment_id == environment_id, + ) + .one_or_none() + ) + return existing_env_role diff --git a/atst/domain/environments.py b/atst/domain/environments.py index ce4da176..16a294fc 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -6,6 +6,7 @@ from atst.models.environment_role import EnvironmentRole, CSPRole from atst.models.project import Project from atst.models.permissions import Permissions from atst.domain.authz import Authorization +from atst.domain.environment_roles import EnvironmentRoles from .exceptions import NotFoundError @@ -67,7 +68,7 @@ class Environments(object): for id_and_role in ids_and_roles: new_role = id_and_role["role"] environment = Environments.get(id_and_role["id"]) - env_role = EnvironmentRole.get(workspace_user.user_id, id_and_role["id"]) + env_role = EnvironmentRoles.get(workspace_user.user_id, id_and_role["id"]) if env_role: env_role.role = new_role else: diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index f9a52957..12a21573 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -16,7 +16,7 @@ from atst.domain.reports import Reports from atst.domain.workspaces import Workspaces from atst.domain.workspace_users import WorkspaceUsers from atst.domain.environments import Environments -from atst.models.environment_role import EnvironmentRole +from atst.domain.environment_roles import EnvironmentRoles from atst.forms.new_project import NewProjectForm from atst.forms.new_member import NewMemberForm from atst.forms.edit_member import EditMemberForm @@ -224,7 +224,7 @@ def view_member(workspace_id, member_id): member=member, projects=projects, form=form, - EnvironmentRole=EnvironmentRole, + EnvironmentRoles=EnvironmentRoles, ) diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index 31252b1e..a9139ade 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -64,7 +64,7 @@
      {% for env in project.environments %} - {% set role = EnvironmentRole.get(member.user_id, env.id).role %} + {% set role = EnvironmentRoles.get(member.user_id, env.id).role %}
    • From 3a978a1673b2fa1efb37041712f76f85e57de34b Mon Sep 17 00:00:00 2001 From: Montana Date: Fri, 21 Sep 2018 14:44:32 -0400 Subject: [PATCH 26/34] Fix bugs and write a test --- atst/domain/environments.py | 10 +++---- atst/domain/projects.py | 4 +-- atst/models/environment_role.py | 12 --------- atst/routes/workspaces.py | 4 +-- tests/domain/test_environments.py | 45 +++++++++++++++++++++++++++++++ 5 files changed, 54 insertions(+), 21 deletions(-) create mode 100644 tests/domain/test_environments.py diff --git a/atst/domain/environments.py b/atst/domain/environments.py index 16a294fc..9ba60f53 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -2,7 +2,7 @@ from sqlalchemy.orm.exc import NoResultFound from atst.database import db from atst.models.environment import Environment -from atst.models.environment_role import EnvironmentRole, CSPRole +from atst.models.environment_role import EnvironmentRole from atst.models.project import Project from atst.models.permissions import Permissions from atst.domain.authz import Authorization @@ -27,9 +27,9 @@ class Environments(object): db.session.commit() @classmethod - def add_member(cls, user, environment, member, role=CSPRole.NONSENSE_ROLE): + def add_member(cls, user, environment, member, role=None): environment_user = EnvironmentRole( - user=member, environment=environment, role=role.value + user=member, environment=environment, role=role ) db.session.add(environment_user) db.session.commit() @@ -57,9 +57,9 @@ class Environments(object): return env @classmethod - def update_environment_role(cls, ids_and_roles, workspace_user): + def update_environment_role(cls, user, ids_and_roles, workspace_user): Authorization.check_workspace_permission( - workspace_user.user, + user, workspace_user.workspace, Permissions.ADD_AND_ASSIGN_CSP_ROLES, "assign environment roles", diff --git a/atst/domain/projects.py b/atst/domain/projects.py index 94ad6a39..cac6d4fa 100644 --- a/atst/domain/projects.py +++ b/atst/domain/projects.py @@ -51,9 +51,9 @@ class Projects(object): ) @classmethod - def get_all(cls, workspace_user, workspace): + def get_all(cls, user, workspace_user, workspace): Authorization.check_workspace_permission( - workspace_user.user, + user, workspace, Permissions.VIEW_APPLICATION_IN_WORKSPACE, "view project in workspace", diff --git a/atst/models/environment_role.py b/atst/models/environment_role.py index f396d9cd..bbfe0710 100644 --- a/atst/models/environment_role.py +++ b/atst/models/environment_role.py @@ -26,18 +26,6 @@ class EnvironmentRole(Base, mixins.TimestampsMixin): user_id = Column(UUID(as_uuid=True), ForeignKey("users.id"), nullable=False) user = relationship("User", backref="environment_roles") - @classmethod - def get(cls, user_id, environment_id): - existing_env_role = ( - db.session.query(EnvironmentRole) - .filter( - EnvironmentRole.user_id == user_id, - EnvironmentRole.environment_id == environment_id, - ) - .one_or_none() - ) - return existing_env_role - Index( "environments_role_user_environment", diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index 12a21573..021159d8 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -216,7 +216,7 @@ def view_member(workspace_id, member_id): "edit this workspace user", ) member = WorkspaceUsers.get(workspace_id, member_id) - projects = Projects.get_all(member, workspace) + projects = Projects.get_all(g.current_user, member, workspace) form = EditMemberForm(workspace_role=member.role, environment_role="") return render_template( "workspaces/members/edit.html", @@ -259,7 +259,7 @@ def update_member(workspace_id, member_id): ) new_role_name = member.role_displayname - Environments.update_environment_role(ids_and_roles, member) + Environments.update_environment_role(g.current_user, ids_and_roles, member) return redirect( url_for( diff --git a/tests/domain/test_environments.py b/tests/domain/test_environments.py new file mode 100644 index 00000000..d4ddce37 --- /dev/null +++ b/tests/domain/test_environments.py @@ -0,0 +1,45 @@ +import pytest +from uuid import uuid4 + +from atst.domain.environments import Environments +from atst.domain.environment_roles import EnvironmentRoles +from atst.domain.projects import Projects +from atst.domain.workspaces import Workspaces +from atst.domain.workspace_users import WorkspaceUsers +from atst.domain.exceptions import NotFoundError + +from tests.factories import RequestFactory, UserFactory + + +def test_update_environment_roles(): + owner = UserFactory.create() + developer_data = { + "dod_id": "1234567890", + "first_name": "Test", + "last_name": "User", + "email": "test.user@mail.com", + "workspace_role": "developer", + } + + workspace = Workspaces.create(RequestFactory.create(creator=owner)) + workspace_user = Workspaces.create_member(owner, workspace, developer_data) + project = Projects.create( + owner, workspace, "my test project", "It's mine.", ["dev", "staging", "prod"] + ) + + dev_env = project.environments[0] + staging_env = project.environments[1] + Environments.add_member(owner, dev_env, workspace_user.user, role="devops") + Environments.add_member(owner, staging_env, workspace_user.user, role="developer") + + new_ids_and_roles = [ + {"id": dev_env.id, "role": "billing_admin"}, + {"id": staging_env.id, "role": "developer"}, + ] + + Environments.update_environment_role(owner, new_ids_and_roles, workspace_user) + new_dev_env_role = EnvironmentRoles.get(workspace_user.user.id, dev_env.id) + staging_env_role = EnvironmentRoles.get(workspace_user.user.id, staging_env.id) + + assert new_dev_env_role.role == "billing_admin" + assert staging_env_role.role == "developer" From 90657e213a1f9aa68f90b921ce94ad08a5c9ea3b Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 24 Sep 2018 10:08:43 -0400 Subject: [PATCH 27/34] Update to FlaskForm --- atst/forms/edit_member.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/atst/forms/edit_member.py b/atst/forms/edit_member.py index 3fa3ed3c..3e3ebd42 100644 --- a/atst/forms/edit_member.py +++ b/atst/forms/edit_member.py @@ -1,4 +1,4 @@ -from flask_wtf import Form +from flask_wtf import FlaskForm from wtforms.validators import Optional from atst.forms.fields import SelectField @@ -6,7 +6,7 @@ from atst.forms.fields import SelectField from .data import WORKSPACE_ROLES, ENVIRONMENT_ROLES -class EditMemberForm(Form): +class EditMemberForm(FlaskForm): workspace_role = SelectField( "Workspace Role", choices=WORKSPACE_ROLES, validators=[Optional()] From 54af62a58a89b5cd8b8d9daed9b74e1f3fe4b71b Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 24 Sep 2018 11:13:03 -0400 Subject: [PATCH 28/34] Test workspace update_member route --- atst/forms/edit_member.py | 4 +-- tests/routes/test_workspaces.py | 57 +++++++++++++++++++++++++++++++++ 2 files changed, 59 insertions(+), 2 deletions(-) diff --git a/atst/forms/edit_member.py b/atst/forms/edit_member.py index 3e3ebd42..b45eef5b 100644 --- a/atst/forms/edit_member.py +++ b/atst/forms/edit_member.py @@ -1,5 +1,5 @@ from flask_wtf import FlaskForm -from wtforms.validators import Optional +from wtforms.validators import Optional, Required from atst.forms.fields import SelectField @@ -9,7 +9,7 @@ from .data import WORKSPACE_ROLES, ENVIRONMENT_ROLES class EditMemberForm(FlaskForm): workspace_role = SelectField( - "Workspace Role", choices=WORKSPACE_ROLES, validators=[Optional()] + "Workspace Role", choices=WORKSPACE_ROLES, validators=[Required()] ) environment_role = SelectField( diff --git a/tests/routes/test_workspaces.py b/tests/routes/test_workspaces.py index fcab09d2..31dce11f 100644 --- a/tests/routes/test_workspaces.py +++ b/tests/routes/test_workspaces.py @@ -2,6 +2,10 @@ from flask import url_for from tests.factories import UserFactory, WorkspaceFactory from atst.domain.workspaces import Workspaces +from atst.domain.workspace_users import WorkspaceUsers +from atst.domain.projects import Projects +from atst.domain.environments import Environments +from atst.domain.environment_roles import EnvironmentRoles from atst.models.workspace_user import WorkspaceUser @@ -67,3 +71,56 @@ def test_update_workspace_name(client, user_session): ) assert response.status_code == 200 assert workspace.name == "a cool new name" + + +def test_update_member_workspace_role(client, user_session): + owner = UserFactory.create() + workspace = WorkspaceFactory.create() + Workspaces._create_workspace_role(owner, workspace, "admin") + user = UserFactory.create() + member = WorkspaceUsers.add(user, workspace.id, "developer") + user_session(owner) + response = client.post( + url_for( + "workspaces.update_member", workspace_id=workspace.id, member_id=user.id + ), + data={"workspace_role": "security_auditor"}, + follow_redirects=True, + ) + assert response.status_code == 200 + assert member.role == "security_auditor" + + +def test_update_member_environment_role(client, user_session): + owner = UserFactory.create() + workspace = WorkspaceFactory.create() + Workspaces._create_workspace_role(owner, workspace, "admin") + + user = UserFactory.create() + member = WorkspaceUsers.add(user, workspace.id, "developer") + project = Projects.create( + owner, + workspace, + "Snazzy Project", + "A new project for me and my friends", + {"env1", "env2"}, + ) + env1_id = project.environments[0].id + env2_id = project.environments[1].id + for env in project.environments: + Environments.add_member(owner, env, user, "developer") + user_session(owner) + response = client.post( + url_for( + "workspaces.update_member", workspace_id=workspace.id, member_id=user.id + ), + data={ + "workspace_role": "developer", + "env_" + str(env1_id): "security_auditor", + "env_" + str(env2_id): "devops", + }, + follow_redirects=True, + ) + assert response.status_code == 200 + assert EnvironmentRoles.get(user.id, env1_id).role == "security_auditor" + assert EnvironmentRoles.get(user.id, env2_id).role == "devops" From 83c4483c42f8c0f02570615ee558ae0290cf2f13 Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 24 Sep 2018 11:17:57 -0400 Subject: [PATCH 29/34] Pass environment role choices directly from the view to the template instead of via the form --- atst/forms/edit_member.py | 10 ++++------ atst/routes/workspaces.py | 2 ++ templates/workspaces/members/edit.html | 4 ++-- 3 files changed, 8 insertions(+), 8 deletions(-) diff --git a/atst/forms/edit_member.py b/atst/forms/edit_member.py index b45eef5b..83333632 100644 --- a/atst/forms/edit_member.py +++ b/atst/forms/edit_member.py @@ -1,17 +1,15 @@ from flask_wtf import FlaskForm -from wtforms.validators import Optional, Required +from wtforms.validators import Required from atst.forms.fields import SelectField -from .data import WORKSPACE_ROLES, ENVIRONMENT_ROLES +from .data import WORKSPACE_ROLES class EditMemberForm(FlaskForm): + # This form also accepts a field for each environment in each project + # that the user is a member of workspace_role = SelectField( "Workspace Role", choices=WORKSPACE_ROLES, validators=[Required()] ) - - environment_role = SelectField( - "Environment Role", choices=ENVIRONMENT_ROLES, validators=[Optional()] - ) diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index 021159d8..94d892e1 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -21,6 +21,7 @@ from atst.forms.new_project import NewProjectForm from atst.forms.new_member import NewMemberForm from atst.forms.edit_member import EditMemberForm from atst.forms.workspace import WorkspaceForm +from atst.forms.data import ENVIRONMENT_ROLES from atst.domain.authz import Authorization from atst.models.permissions import Permissions @@ -224,6 +225,7 @@ def view_member(workspace_id, member_id): member=member, projects=projects, form=form, + choices=ENVIRONMENT_ROLES, EnvironmentRoles=EnvironmentRoles, ) diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index a9139ade..820d8a8e 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -67,7 +67,7 @@ {% set role = EnvironmentRoles.get(member.user_id, env.id).role %}
    • - +
      {{ env.name }} @@ -81,7 +81,7 @@ {% call Modal(name=env.name + 'RolesModal', dismissable=False) %}
        - {% for choice in form.environment_role.choices %} + {% for choice in choices %}
      • {% if choice[0] != "" %} From d05edb4aa51d1d62c56062cf9edf991a414884f7 Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 24 Sep 2018 11:23:53 -0400 Subject: [PATCH 30/34] Remove unused imports leftover from rebase --- atst/models/environment_role.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/atst/models/environment_role.py b/atst/models/environment_role.py index bbfe0710..594b46f3 100644 --- a/atst/models/environment_role.py +++ b/atst/models/environment_role.py @@ -4,8 +4,6 @@ from sqlalchemy.dialects.postgresql import UUID from sqlalchemy.orm import relationship from atst.models import Base, types, mixins -from atst.database import db -from .types import Id class CSPRole(Enum): From e1767488c7e7d84e4b361c2fe69eab8b17599319 Mon Sep 17 00:00:00 2001 From: Montana Date: Mon, 24 Sep 2018 17:14:35 -0400 Subject: [PATCH 31/34] Fix seed script bug --- atst/routes/workspaces.py | 2 +- script/seed_sample.py | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index 94d892e1..effaf9e7 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -218,7 +218,7 @@ def view_member(workspace_id, member_id): ) member = WorkspaceUsers.get(workspace_id, member_id) projects = Projects.get_all(g.current_user, member, workspace) - form = EditMemberForm(workspace_role=member.role, environment_role="") + form = EditMemberForm(workspace_role=member.role) return render_template( "workspaces/members/edit.html", workspace=workspace, diff --git a/script/seed_sample.py b/script/seed_sample.py index 237b10a3..1e7b9f46 100644 --- a/script/seed_sample.py +++ b/script/seed_sample.py @@ -27,7 +27,7 @@ WORKSPACE_USERS = [ "first_name": "Mario", "last_name": "Hudson", "email": "hudson@mil.gov", - "workspace_role": "ccpo", + "workspace_role": "billing_auditor", "dod_id": "0000000002", }, { From 8fd1d93f2e4695c5dcf442f6ce5c1cfdde1aee11 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 25 Sep 2018 08:36:50 -0400 Subject: [PATCH 32/34] Prevent saving environment roles as empty strings --- atst/domain/environments.py | 2 +- atst/domain/projects.py | 3 --- atst/routes/workspaces.py | 3 ++- templates/workspaces/members/edit.html | 2 -- 4 files changed, 3 insertions(+), 7 deletions(-) diff --git a/atst/domain/environments.py b/atst/domain/environments.py index 9ba60f53..e4e9e5b6 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -27,7 +27,7 @@ class Environments(object): db.session.commit() @classmethod - def add_member(cls, user, environment, member, role=None): + def add_member(cls, environment, member, role): environment_user = EnvironmentRole( user=member, environment=environment, role=role ) diff --git a/atst/domain/projects.py b/atst/domain/projects.py index cac6d4fa..36c1168d 100644 --- a/atst/domain/projects.py +++ b/atst/domain/projects.py @@ -14,9 +14,6 @@ class Projects(object): project = Project(workspace=workspace, name=name, description=description) Environments.create_many(project, environment_names) - for environment in project.environments: - Environments.add_member(user, environment, user) - db.session.add(project) db.session.commit() diff --git a/atst/routes/workspaces.py b/atst/routes/workspaces.py index effaf9e7..2a51f499 100644 --- a/atst/routes/workspaces.py +++ b/atst/routes/workspaces.py @@ -249,7 +249,8 @@ def update_member(workspace_id, member_id): if re.match("env_", entry): env_id = entry[4:] env_role = form_dict[entry] - ids_and_roles.append({"id": env_id, "role": env_role}) + if env_role: + ids_and_roles.append({"id": env_id, "role": env_role}) form = EditMemberForm(http_request.form) diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index 820d8a8e..79ba5e06 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -74,8 +74,6 @@
        -
        {{ form.data["environment_role"] }}
        - {% call Modal(name=env.name + 'RolesModal', dismissable=False) %} From 18776aa71c6e91bc9c60498262d262c255ba7710 Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 25 Sep 2018 08:59:44 -0400 Subject: [PATCH 33/34] Fix tests --- atst/domain/environments.py | 4 ++-- tests/domain/test_environments.py | 4 ++-- tests/domain/test_workspaces.py | 2 +- tests/models/test_environments.py | 2 +- tests/models/test_workspace_user.py | 2 +- tests/routes/test_workspaces.py | 2 +- 6 files changed, 8 insertions(+), 8 deletions(-) diff --git a/atst/domain/environments.py b/atst/domain/environments.py index e4e9e5b6..eebc6c15 100644 --- a/atst/domain/environments.py +++ b/atst/domain/environments.py @@ -27,9 +27,9 @@ class Environments(object): db.session.commit() @classmethod - def add_member(cls, environment, member, role): + def add_member(cls, environment, user, role): environment_user = EnvironmentRole( - user=member, environment=environment, role=role + user=user, environment=environment, role=role ) db.session.add(environment_user) db.session.commit() diff --git a/tests/domain/test_environments.py b/tests/domain/test_environments.py index d4ddce37..2b7f4995 100644 --- a/tests/domain/test_environments.py +++ b/tests/domain/test_environments.py @@ -29,8 +29,8 @@ def test_update_environment_roles(): dev_env = project.environments[0] staging_env = project.environments[1] - Environments.add_member(owner, dev_env, workspace_user.user, role="devops") - Environments.add_member(owner, staging_env, workspace_user.user, role="developer") + Environments.add_member(dev_env, workspace_user.user, "devops") + Environments.add_member(staging_env, workspace_user.user, "developer") new_ids_and_roles = [ {"id": dev_env.id, "role": "billing_admin"}, diff --git a/tests/domain/test_workspaces.py b/tests/domain/test_workspaces.py index 06d3f836..90464b93 100644 --- a/tests/domain/test_workspaces.py +++ b/tests/domain/test_workspaces.py @@ -169,7 +169,7 @@ def test_scoped_workspace_only_returns_a_users_projects_and_environments( ) developer = UserFactory.from_atat_role("developer") dev_environment = Environments.add_member( - workspace_owner, new_project.environments[0], developer + new_project.environments[0], developer, "developer" ) scoped_workspace = Workspaces.get(developer, workspace.id) diff --git a/tests/models/test_environments.py b/tests/models/test_environments.py index 05be623f..32faeef2 100644 --- a/tests/models/test_environments.py +++ b/tests/models/test_environments.py @@ -14,5 +14,5 @@ def test_add_user_to_environment(): ) dev_environment = project.environments[0] - dev_environment = Environments.add_member(owner, dev_environment, developer) + dev_environment = Environments.add_member(dev_environment, developer, "developer") assert developer in dev_environment.users diff --git a/tests/models/test_workspace_user.py b/tests/models/test_workspace_user.py index fac20d68..da1f08d0 100644 --- a/tests/models/test_workspace_user.py +++ b/tests/models/test_workspace_user.py @@ -36,7 +36,7 @@ def test_has_environment_roles(): project = Projects.create( owner, workspace, "my test project", "It's mine.", ["dev", "staging", "prod"] ) - Environments.add_member(owner, project.environments[0], workspace_user.user) + Environments.add_member(project.environments[0], workspace_user.user, "developer") assert workspace_user.has_environment_roles diff --git a/tests/routes/test_workspaces.py b/tests/routes/test_workspaces.py index 31dce11f..3523d207 100644 --- a/tests/routes/test_workspaces.py +++ b/tests/routes/test_workspaces.py @@ -108,7 +108,7 @@ def test_update_member_environment_role(client, user_session): env1_id = project.environments[0].id env2_id = project.environments[1].id for env in project.environments: - Environments.add_member(owner, env, user, "developer") + Environments.add_member(env, user, "developer") user_session(owner) response = client.post( url_for( From e17ede451415efe1f62a2a31406109fe6ff673ad Mon Sep 17 00:00:00 2001 From: Montana Date: Tue, 25 Sep 2018 11:12:52 -0400 Subject: [PATCH 34/34] No destructuring in IE10 --- templates/workspaces/members/edit.html | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/templates/workspaces/members/edit.html b/templates/workspaces/members/edit.html index 79ba5e06..bc40122c 100644 --- a/templates/workspaces/members/edit.html +++ b/templates/workspaces/members/edit.html @@ -52,16 +52,16 @@ {% for project in projects %}
        -