Merge pull request #237 from dod-ccpo/add-workspace-member
Add a member to a workspace
This commit is contained in:
commit
be715bc377
@ -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 = []
|
||||
|
@ -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)
|
||||
|
@ -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.",
|
||||
},
|
||||
),
|
||||
]
|
||||
|
@ -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
|
||||
|
@ -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)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
@ -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"]
|
||||
)
|
||||
|
||||
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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>
|
||||
|
@ -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)
|
||||
|
Loading…
x
Reference in New Issue
Block a user