Merge pull request #237 from dod-ccpo/add-workspace-member

Add a member to a workspace
This commit is contained in:
richard-dds 2018-09-04 10:23:01 -04:00 committed by GitHub
commit be715bc377
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
11 changed files with 127 additions and 22 deletions

View File

@ -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 = []

View File

@ -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)

View File

@ -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.",
},
),
]

View File

@ -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

View File

@ -101,3 +101,21 @@ 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/<workspace_id>/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():
new_member = Workspaces.create_member(g.current_user, workspace, form.data)
return redirect(
url_for(
"workspaces.workspace_members",
workspace_id=workspace.id,
newMemberName=new_member.user.full_name,
)
)
else:
return render_template("member_new.html", workspace=workspace, form=form)

View File

@ -26,11 +26,14 @@ 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
}
}
},

View File

@ -37,7 +37,8 @@ def seed_db():
Projects.create(
workspace=workspace,
name="First Project",
description="This is our first project."
description="This is our first project.",
environment_names=["dev", "staging", "prod"]
)

View File

@ -44,13 +44,13 @@
v-bind:checked='value === choice[0]'
v-on:change='change'/>
<label v-bind:for="'{{ field.name }}_' + choice[0]">
<template v-if='choices[2]'>
<template v-if='choice[1].description'>
<dl>
<dt v-html='choice[1]'></dt>
<dd v-html='choice[2]'></dd>
<dt v-html='choice[1].name'></dt>
<dd v-html='choice[1].description'></dd>
</dl>
</template>
<span v-else v-html='choice[1]'>
<span v-else v-html='choice[1].name'>
</label>
</template>
</li>

View File

@ -7,7 +7,8 @@
{% block content %}
<form method="POST" action="" autocomplete="false">
<form method="POST" action="{{ url_for('workspaces.create_member', workspace_id=workspace.id) }}" autocomplete="false">
{{ form.csrf_token }}
<div class="panel">
@ -27,9 +28,7 @@
<div class='action-group'>
<a href='#' class='action-group__action usa-button usa-button-big'>
Add User
</a>
<button class="usa-button usa-button-big usa-button-primary" tabindex="0">Add User</button>
<a href='#' class='action-group__action icon-link'>
{{ Icon('x') }}
<span>Cancel</span>

View File

@ -1,6 +1,7 @@
{% extends "base_workspace.html" %}
{% from "components/empty_state.html" import EmptyState %}
{% from "components/alert.html" import Alert %}
{% block workspace_content %}
@ -19,6 +20,19 @@
{% else %}
{% set new_member_name = request.args.get("newMemberName") %}
{% if new_member_name %}
{% set message -%}
<p>{{ new_member_name }} was successfully invited via email to this workspace. They do not yet have access to any environments.</p>
<p><a href="#">Add environment access.</a></p>
{%- endset %}
{{ Alert('Member added successfully',
message=message,
level='success'
) }}
{% endif %}
<form class='search-bar'>
<div class='usa-input search-input'>
<label for='members-search'>Search members by name</label>

View File

@ -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)