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
|
@classmethod
|
||||||
def add(cls, user, workspace_id, role_name):
|
def add(cls, user, workspace_id, role_name):
|
||||||
role = Roles.get(role_name)
|
role = Roles.get(role_name)
|
||||||
|
|
||||||
|
new_workspace_role = None
|
||||||
try:
|
try:
|
||||||
existing_workspace_role = (
|
existing_workspace_role = (
|
||||||
db.session.query(WorkspaceRole)
|
db.session.query(WorkspaceRole)
|
||||||
@ -53,6 +55,8 @@ class WorkspaceUsers(object):
|
|||||||
db.session.add(user)
|
db.session.add(user)
|
||||||
db.session.commit()
|
db.session.commit()
|
||||||
|
|
||||||
|
return WorkspaceUser(user, new_workspace_role)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def add_many(cls, workspace_id, workspace_user_dicts):
|
def add_many(cls, workspace_id, workspace_user_dicts):
|
||||||
workspace_users = []
|
workspace_users = []
|
||||||
|
@ -7,6 +7,8 @@ from atst.domain.exceptions import NotFoundError, UnauthorizedError
|
|||||||
from atst.domain.roles import Roles
|
from atst.domain.roles import Roles
|
||||||
from atst.domain.authz import Authorization
|
from atst.domain.authz import Authorization
|
||||||
from atst.models.permissions import Permissions
|
from atst.models.permissions import Permissions
|
||||||
|
from atst.domain.users import Users
|
||||||
|
from atst.domain.workspace_users import WorkspaceUsers
|
||||||
|
|
||||||
|
|
||||||
class Workspaces(object):
|
class Workspaces(object):
|
||||||
@ -61,6 +63,24 @@ class Workspaces(object):
|
|||||||
)
|
)
|
||||||
return workspaces
|
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
|
@classmethod
|
||||||
def _create_workspace_role(cls, user, workspace, role_name):
|
def _create_workspace_role(cls, user, workspace, role_name):
|
||||||
role = Roles.get(role_name)
|
role = Roles.get(role_name)
|
||||||
|
@ -106,27 +106,37 @@ COMPLETION_DATE_RANGES = [
|
|||||||
WORKSPACE_ROLES = [
|
WORKSPACE_ROLES = [
|
||||||
(
|
(
|
||||||
"owner",
|
"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",
|
"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",
|
||||||
"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",
|
||||||
"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",
|
||||||
"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)
|
return set(workspace_permissions).union(atat_permissions)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def workspace(self):
|
||||||
|
return self.workspace_role.workspace
|
||||||
|
|
||||||
def workspace_id(self):
|
def workspace_id(self):
|
||||||
return self.workspace_role.workspace_id
|
return self.workspace_role.workspace_id
|
||||||
|
@ -101,3 +101,21 @@ def new_member(workspace_id):
|
|||||||
workspace = Workspaces.get(g.current_user, workspace_id)
|
workspace = Workspaces.get(g.current_user, workspace_id)
|
||||||
form = NewMemberForm()
|
form = NewMemberForm()
|
||||||
return render_template("member_new.html", workspace=workspace, form=form)
|
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: {
|
computed: {
|
||||||
label: function () {
|
label: function () {
|
||||||
return this.value
|
if (this.value) {
|
||||||
? this.choices.find((choice) => {
|
const selectedChoice = this.choices.find((choice) => {
|
||||||
return this.value === choice[0]
|
return this.value === choice[0]
|
||||||
})[1]
|
})[1]
|
||||||
: this.defaultLabel
|
return selectedChoice.name
|
||||||
|
} else {
|
||||||
|
return this.defaultLabel
|
||||||
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
@ -37,7 +37,8 @@ def seed_db():
|
|||||||
Projects.create(
|
Projects.create(
|
||||||
workspace=workspace,
|
workspace=workspace,
|
||||||
name="First Project",
|
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-bind:checked='value === choice[0]'
|
||||||
v-on:change='change'/>
|
v-on:change='change'/>
|
||||||
<label v-bind:for="'{{ field.name }}_' + choice[0]">
|
<label v-bind:for="'{{ field.name }}_' + choice[0]">
|
||||||
<template v-if='choices[2]'>
|
<template v-if='choice[1].description'>
|
||||||
<dl>
|
<dl>
|
||||||
<dt v-html='choice[1]'></dt>
|
<dt v-html='choice[1].name'></dt>
|
||||||
<dd v-html='choice[2]'></dd>
|
<dd v-html='choice[1].description'></dd>
|
||||||
</dl>
|
</dl>
|
||||||
</template>
|
</template>
|
||||||
<span v-else v-html='choice[1]'>
|
<span v-else v-html='choice[1].name'>
|
||||||
</label>
|
</label>
|
||||||
</template>
|
</template>
|
||||||
</li>
|
</li>
|
||||||
|
@ -7,7 +7,8 @@
|
|||||||
|
|
||||||
{% block content %}
|
{% 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">
|
<div class="panel">
|
||||||
|
|
||||||
@ -27,9 +28,7 @@
|
|||||||
|
|
||||||
|
|
||||||
<div class='action-group'>
|
<div class='action-group'>
|
||||||
<a href='#' class='action-group__action usa-button usa-button-big'>
|
<button class="usa-button usa-button-big usa-button-primary" tabindex="0">Add User</button>
|
||||||
Add User
|
|
||||||
</a>
|
|
||||||
<a href='#' class='action-group__action icon-link'>
|
<a href='#' class='action-group__action icon-link'>
|
||||||
{{ Icon('x') }}
|
{{ Icon('x') }}
|
||||||
<span>Cancel</span>
|
<span>Cancel</span>
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
{% extends "base_workspace.html" %}
|
{% extends "base_workspace.html" %}
|
||||||
|
|
||||||
{% from "components/empty_state.html" import EmptyState %}
|
{% from "components/empty_state.html" import EmptyState %}
|
||||||
|
{% from "components/alert.html" import Alert %}
|
||||||
|
|
||||||
{% block workspace_content %}
|
{% block workspace_content %}
|
||||||
|
|
||||||
@ -19,6 +20,19 @@
|
|||||||
|
|
||||||
{% else %}
|
{% 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'>
|
<form class='search-bar'>
|
||||||
<div class='usa-input search-input'>
|
<div class='usa-input search-input'>
|
||||||
<label for='members-search'>Search members by name</label>
|
<label for='members-search'>Search members by name</label>
|
||||||
|
@ -87,3 +87,35 @@ def test_get_for_update_blocks_developer():
|
|||||||
|
|
||||||
with pytest.raises(UnauthorizedError):
|
with pytest.raises(UnauthorizedError):
|
||||||
Workspaces.get_for_update(developer, workspace.id)
|
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