Merge pull request #252 from dod-ccpo/view-member-list-workspace-owner

View member list as workspace owner
This commit is contained in:
richard-dds 2018-09-06 16:53:19 -04:00 committed by GitHub
commit a96af2d095
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 148 additions and 12 deletions

View File

@ -0,0 +1,53 @@
"""add view_workspace_members_permission
Revision ID: ad30159ef19b
Revises: 2c2a2af465d3
Create Date: 2018-09-05 11:17:17.204089
"""
from alembic import op
from sqlalchemy.orm.session import Session
from atst.models.role import Role
from atst.models.permissions import Permissions
# revision identifiers, used by Alembic.
revision = 'ad30159ef19b'
down_revision = 'c1d074288e99'
branch_labels = None
depends_on = None
def upgrade():
session = Session(bind=op.get_bind())
all_roles_but_default = session.query(Role).filter(Role.name != "default").all()
for role in all_roles_but_default:
role.add_permission(Permissions.VIEW_WORKSPACE)
session.add(role)
owner_and_ccpo = session.query(Role).filter(Role.name.in_(["owner", "ccpo", "admin"])).all()
for role in owner_and_ccpo:
role.add_permission(Permissions.VIEW_WORKSPACE_MEMBERS)
session.add(role)
session.flush()
session.commit()
def downgrade():
session = Session(bind=op.get_bind())
all_roles_but_default = session.query(Role).filter(Role.name != "default").all()
for role in all_roles_but_default:
role.remove_permission(Permissions.VIEW_WORKSPACE)
session.add(role)
owner_and_ccpo = session.query(Role).filter(Role.name.in_(["owner", "ccpo"])).all()
for role in owner_and_ccpo:
role.remove_permission(Permissions.VIEW_WORKSPACE_MEMBERS)
session.add(role)
session.flush()
session.commit()

View File

@ -3,7 +3,7 @@ from sqlalchemy.orm.exc import NoResultFound
from atst.database import db
from atst.models.workspace import Workspace
from atst.models.workspace_role import WorkspaceRole
from atst.domain.exceptions import NotFoundError, UnauthorizedError
from atst.domain.exceptions import NotFoundError
from atst.domain.roles import Roles
from atst.domain.authz import Authorization
from atst.models.permissions import Permissions
@ -25,19 +25,16 @@ class Workspaces(object):
@classmethod
def get(cls, user, workspace_id):
try:
workspace = db.session.query(Workspace).filter_by(id=workspace_id).one()
except NoResultFound:
raise NotFoundError("workspace")
if not Authorization.is_in_workspace(user, workspace):
raise UnauthorizedError(user, "get workspace")
workspace = Workspaces._get(workspace_id)
Authorization.check_workspace_permission(
user, workspace, Permissions.VIEW_WORKSPACE, "get workspace"
)
return workspace
@classmethod
def get_for_update(cls, user, workspace_id):
workspace = Workspaces.get(user, workspace_id)
workspace = Workspaces._get(workspace_id)
Authorization.check_workspace_permission(
user, workspace, Permissions.ADD_APPLICATION_IN_WORKSPACE, "add project"
)
@ -53,6 +50,18 @@ class Workspaces(object):
return workspace
@classmethod
def get_with_members(cls, user, workspace_id):
workspace = Workspaces._get(workspace_id)
Authorization.check_workspace_permission(
user,
workspace,
Permissions.VIEW_WORKSPACE_MEMBERS,
"view workspace members",
)
return workspace
@classmethod
def get_many(cls, user):
workspaces = (
@ -100,3 +109,12 @@ class Workspaces(object):
workspace_role = WorkspaceRole(user=user, role=role, workspace=workspace)
db.session.add(workspace_role)
return workspace_role
@classmethod
def _get(cls, workspace_id):
try:
workspace = db.session.query(Workspace).filter_by(id=workspace_id).one()
except NoResultFound:
raise NotFoundError("workspace")
return workspace

View File

@ -23,6 +23,8 @@ class Permissions(object):
DEACTIVATE_WORKSPACE = "deactivate_workspace"
VIEW_ATAT_PERMISSIONS = "view_atat_permissions"
TRANSFER_OWNERSHIP_OF_WORKSPACE = "transfer_ownership_of_workspace"
VIEW_WORKSPACE_MEMBERS = "view_workspace_members"
VIEW_WORKSPACE = "view_workspace"
ADD_APPLICATION_IN_WORKSPACE = "add_application_in_workspace"
DELETE_APPLICATION_IN_WORKSPACE = "delete_application_in_workspace"

View File

@ -1,5 +1,6 @@
from sqlalchemy import String, Column
from sqlalchemy.dialects.postgresql import ARRAY
from sqlalchemy.orm.attributes import flag_modified
from atst.models import Base
from .types import Id
@ -12,3 +13,15 @@ class Role(Base):
name = Column(String, index=True, unique=True)
description = Column(String)
permissions = Column(ARRAY(String), index=True, server_default="{}")
def add_permission(self, permission):
perms_set = set(self.permissions)
perms_set.add(permission)
self.permissions = list(perms_set)
flag_modified(self, "permissions")
def remove_permission(self, permission):
perms_set = set(self.permissions)
perms_set.discard(permission)
self.permissions = list(perms_set)
flag_modified(self, "permissions")

View File

@ -4,8 +4,8 @@ from sqlalchemy.orm import relationship
from atst.models import Base
from atst.models.types import Id
from atst.models.mixins import TimestampsMixin
from atst.utils import first_or_none
from atst.models.workspace_user import WorkspaceUser
from atst.utils import first_or_none
class Workspace(Base, TimestampsMixin):

View File

@ -38,7 +38,7 @@ class WorkspaceUser(object):
@property
def status(self):
return "radical"
return "active"
@property
def has_environment_roles(self):

View File

@ -63,7 +63,7 @@ def show_workspace(workspace_id):
@bp.route("/workspaces/<workspace_id>/members")
def workspace_members(workspace_id):
workspace = Workspaces.get(g.current_user, workspace_id)
workspace = Workspaces.get_with_members(g.current_user, workspace_id)
return render_template("workspaces/members/index.html", workspace=workspace)

View File

@ -15,6 +15,29 @@ from atst.domain.exceptions import AlreadyExistsError
from tests.factories import RequestFactory
from atst.routes.dev import _DEV_USERS as DEV_USERS
WORKSPACE_USERS = [
{
"first_name": "Danny",
"last_name": "Knight",
"email": "knight@mil.gov",
"workspace_role": "developer",
"dod_id": "0000000001"
},
{
"first_name": "Mario",
"last_name": "Hudson",
"email": "hudson@mil.gov",
"workspace_role": "ccpo",
"dod_id": "0000000002"
},
{
"first_name": "Louise",
"last_name": "Greer",
"email": "greer@mil.gov",
"workspace_role": "admin",
"dod_id": "0000000003"
},
]
def seed_db():
users = []
@ -41,6 +64,9 @@ def seed_db():
requests.append(request)
workspace = Workspaces.create(requests[0], name="{}'s workspace".format(user.first_name))
for workspace_user in WORKSPACE_USERS:
Workspaces.create_member(user, workspace, workspace_user)
Projects.create(
workspace=workspace,
name="First Project",

View File

@ -155,3 +155,27 @@ def test_need_permission_to_update_workspace_user_role():
with pytest.raises(UnauthorizedError):
Workspaces.update_member(random_user, workspace, member, role_name)
def test_owner_can_view_workspace_members():
owner = UserFactory.create()
workspace = Workspaces.create(RequestFactory.create(creator=owner))
workspace = Workspaces.get_with_members(owner, workspace.id)
assert workspace
def test_ccpo_can_view_workspace_members():
workspace = Workspaces.create(RequestFactory.create(creator=UserFactory.create()))
ccpo = UserFactory.from_atat_role("ccpo")
workspace = Workspaces.get_with_members(ccpo, workspace.id)
assert workspace
def test_random_user_cannot_view_workspace_members():
workspace = Workspaces.create(RequestFactory.create(creator=UserFactory.create()))
developer = UserFactory.from_atat_role("developer")
with pytest.raises(UnauthorizedError):
workspace = Workspaces.get_with_members(developer, workspace.id)