Allow permissions to be set for teams in worklists and boards
This commit allows permissions to be set for teams in worklists and boards. This allows for much more convenient management of the ACLs of worklists and boards, reducing the work required to make a board usable by a specific project team. Change-Id: Ib5849643d768cba5b2ca5cd5e80ca8dfd2c5e25e
This commit is contained in:
parent
255894392a
commit
4396fc3d5c
@ -33,6 +33,7 @@ from storyboard.common import exception as exc
|
||||
from storyboard.db.api import boards as boards_api
|
||||
from storyboard.db.api import stories as stories_api
|
||||
from storyboard.db.api import tasks as tasks_api
|
||||
from storyboard.db.api import teams as teams_api
|
||||
from storyboard.db.api import timeline_events as events_api
|
||||
from storyboard.db.api import users as users_api
|
||||
from storyboard.db.api import worklists as worklists_api
|
||||
@ -159,11 +160,13 @@ class PermissionsController(rest.RestController):
|
||||
created = boards_api.create_permission(board_id, permission)
|
||||
|
||||
users = [{user.id: user.full_name} for user in created.users]
|
||||
teams = [{team.id: team.name} for team in created.teams]
|
||||
events_api.board_permission_created_event(board_id,
|
||||
user_id,
|
||||
created.id,
|
||||
created.codename,
|
||||
users)
|
||||
users,
|
||||
teams)
|
||||
|
||||
return created
|
||||
else:
|
||||
@ -193,15 +196,22 @@ class PermissionsController(rest.RestController):
|
||||
% permission['codename'])
|
||||
|
||||
old_users = {user.id: user.full_name for user in old.users}
|
||||
old_teams = {team.id: team.name for team in old.teams}
|
||||
|
||||
if boards_api.editable(board, user_id):
|
||||
updated = boards_api.update_permission(board_id, permission)
|
||||
new_users = {user.id: user.full_name for user in updated.users}
|
||||
new_teams = {team.id: team.name for team in updated.teams}
|
||||
|
||||
added = [{id: name} for id, name in six.iteritems(new_users)
|
||||
if id not in old_users]
|
||||
added.extend([{id: name} for id, name in six.iteritems(new_teams)
|
||||
if id not in old_teams])
|
||||
removed = [{id: name} for id, name in six.iteritems(old_users)
|
||||
if id not in new_users]
|
||||
removed.extend([{id: name}
|
||||
for id, name in six.iteritems(old_teams)
|
||||
if id not in new_teams])
|
||||
|
||||
if added or removed:
|
||||
events_api.board_permissions_changed_event(board.id,
|
||||
@ -210,6 +220,7 @@ class PermissionsController(rest.RestController):
|
||||
updated.codename,
|
||||
added,
|
||||
removed)
|
||||
return updated.codename
|
||||
else:
|
||||
raise exc.NotFound(_("Board %s not found") % board_id)
|
||||
|
||||
@ -328,10 +339,18 @@ class BoardsController(rest.RestController):
|
||||
lanes = board_dict.pop('lanes') or []
|
||||
owners = board_dict.pop('owners')
|
||||
users = board_dict.pop('users')
|
||||
if not owners:
|
||||
team_owners = board_dict.pop('team_owners')
|
||||
team_users = board_dict.pop('team_users')
|
||||
if not owners and not team_owners:
|
||||
owners = [user_id]
|
||||
if not owners and team_owners:
|
||||
owners = []
|
||||
if not users:
|
||||
users = []
|
||||
if not team_owners:
|
||||
team_owners = []
|
||||
if not team_users:
|
||||
team_users = []
|
||||
|
||||
# We can't set due dates when creating boards at the moment.
|
||||
if 'due_dates' in board_dict:
|
||||
@ -352,29 +371,37 @@ class BoardsController(rest.RestController):
|
||||
edit_permission = {
|
||||
'name': 'edit_board_%d' % created_board.id,
|
||||
'codename': 'edit_board',
|
||||
'users': owners
|
||||
'users': owners,
|
||||
'teams': team_owners
|
||||
}
|
||||
move_permission = {
|
||||
'name': 'move_cards_%d' % created_board.id,
|
||||
'codename': 'move_cards',
|
||||
'users': users
|
||||
'users': users,
|
||||
'teams': team_users
|
||||
}
|
||||
edit = boards_api.create_permission(created_board.id, edit_permission)
|
||||
move = boards_api.create_permission(created_board.id, move_permission)
|
||||
event_owners = [{id: users_api.user_get(id).full_name}
|
||||
for id in owners]
|
||||
event_team_owners = [{id: teams_api.team_get(id).name}
|
||||
for id in owners]
|
||||
event_users = [{id: users_api.user_get(id).full_name}
|
||||
for id in users]
|
||||
event_team_users = [{id: teams_api.team_get(id).name}
|
||||
for id in users]
|
||||
events_api.board_permission_created_event(created_board.id,
|
||||
user_id,
|
||||
edit.id,
|
||||
edit.codename,
|
||||
event_owners)
|
||||
event_owners,
|
||||
event_team_owners)
|
||||
events_api.board_permission_created_event(created_board.id,
|
||||
user_id,
|
||||
move.id,
|
||||
move.codename,
|
||||
event_users)
|
||||
event_users,
|
||||
event_team_users)
|
||||
|
||||
return wmodels.Board.from_db_model(created_board)
|
||||
|
||||
|
@ -797,6 +797,12 @@ class Worklist(base.APIBase):
|
||||
users = wtypes.ArrayType(int)
|
||||
"""A list of the IDs of the users who can move items in the worklist."""
|
||||
|
||||
team_owners = wtypes.ArrayType(int)
|
||||
"""A list of the IDs of the teams who have full permissions."""
|
||||
|
||||
team_users = wtypes.ArrayType(int)
|
||||
"""A list of the IDs of the teams who can move items in the worklist."""
|
||||
|
||||
items = wtypes.ArrayType(WorklistItem)
|
||||
"""The items in the worklist."""
|
||||
|
||||
@ -853,8 +859,10 @@ class Worklist(base.APIBase):
|
||||
|
||||
@nodoc
|
||||
def resolve_permissions(self, worklist):
|
||||
self.owners = worklists_api.get_owners(worklist)
|
||||
self.users = worklists_api.get_users(worklist)
|
||||
self.owners = worklists_api.get_user_owners(worklist)
|
||||
self.users = worklists_api.get_user_users(worklist)
|
||||
self.team_owners = worklists_api.get_team_owners(worklist)
|
||||
self.team_users = worklists_api.get_team_users(worklist)
|
||||
|
||||
@nodoc
|
||||
def resolve_filters(self, worklist):
|
||||
@ -951,6 +959,12 @@ class Board(base.APIBase):
|
||||
users = wtypes.ArrayType(int)
|
||||
"""A list of the IDs of the users who can move cards in the board."""
|
||||
|
||||
team_owners = wtypes.ArrayType(int)
|
||||
"""A list of the IDs of the teams who have full permissions."""
|
||||
|
||||
team_users = wtypes.ArrayType(int)
|
||||
"""A list of the IDs of the teams who can move cards in the board."""
|
||||
|
||||
@classmethod
|
||||
def sample(cls):
|
||||
return cls(
|
||||
@ -978,7 +992,9 @@ class Board(base.APIBase):
|
||||
items=[]))],
|
||||
due_dates=[],
|
||||
owners=[1],
|
||||
users=[])
|
||||
users=[],
|
||||
team_owners=[1, 2],
|
||||
team_users=[])
|
||||
|
||||
@nodoc
|
||||
def resolve_lanes(self, board, story_cache={}, task_cache={},
|
||||
@ -1007,5 +1023,7 @@ class Board(base.APIBase):
|
||||
@nodoc
|
||||
def resolve_permissions(self, board):
|
||||
"""Resolve the permissions groups of the board."""
|
||||
self.owners = boards_api.get_owners(board)
|
||||
self.users = boards_api.get_users(board)
|
||||
self.owners = boards_api.get_user_owners(board)
|
||||
self.users = boards_api.get_user_users(board)
|
||||
self.team_owners = boards_api.get_team_owners(board)
|
||||
self.team_users = boards_api.get_team_users(board)
|
||||
|
@ -32,6 +32,7 @@ from storyboard.common import decorators
|
||||
from storyboard.common import exception as exc
|
||||
from storyboard.db.api import stories as stories_api
|
||||
from storyboard.db.api import tasks as tasks_api
|
||||
from storyboard.db.api import teams as teams_api
|
||||
from storyboard.db.api import timeline_events as events_api
|
||||
from storyboard.db.api import users as users_api
|
||||
from storyboard.db.api import worklists as worklists_api
|
||||
@ -137,11 +138,13 @@ class PermissionsController(rest.RestController):
|
||||
created = worklists_api.create_permission(worklist_id, permission)
|
||||
|
||||
users = [{user.id: user.full_name} for user in created.users]
|
||||
teams = [{team.id: team.name} for team in created.teams]
|
||||
events_api.worklist_permission_created_event(worklist_id,
|
||||
user_id,
|
||||
created.id,
|
||||
created.codename,
|
||||
users)
|
||||
users,
|
||||
teams)
|
||||
|
||||
return created.codename
|
||||
else:
|
||||
@ -166,6 +169,10 @@ class PermissionsController(rest.RestController):
|
||||
1,
|
||||
2,
|
||||
3
|
||||
],
|
||||
"teams": [
|
||||
1,
|
||||
6
|
||||
]
|
||||
}
|
||||
|
||||
@ -189,16 +196,23 @@ class PermissionsController(rest.RestController):
|
||||
% permission['codename'])
|
||||
|
||||
old_users = {user.id: user.full_name for user in old.users}
|
||||
old_teams = {team.id: team.name for team in old.teams}
|
||||
|
||||
if worklists_api.editable(worklist, user_id):
|
||||
updated = worklists_api.update_permission(
|
||||
worklist_id, permission)
|
||||
new_users = {user.id: user.full_name for user in updated.users}
|
||||
new_teams = {team.id: team.name for team in updated.teams}
|
||||
|
||||
added = [{id: name} for id, name in six.iteritems(new_users)
|
||||
if id not in old_users]
|
||||
added.extend([{id: name} for id, name in six.iteritems(new_teams)
|
||||
if id not in old_teams])
|
||||
removed = [{id: name} for id, name in six.iteritems(old_users)
|
||||
if id not in new_users]
|
||||
removed.extend([{id: name}
|
||||
for id, name in six.iteritems(old_teams)
|
||||
if id not in new_teams])
|
||||
|
||||
if added or removed:
|
||||
events_api.worklist_permissions_changed_event(
|
||||
@ -746,10 +760,18 @@ class WorklistsController(rest.RestController):
|
||||
filters = worklist_dict.pop('filters') or []
|
||||
owners = worklist_dict.pop('owners')
|
||||
users = worklist_dict.pop('users')
|
||||
if not owners:
|
||||
team_owners = worklist_dict.pop('team_owners')
|
||||
team_users = worklist_dict.pop('team_users')
|
||||
if not owners and not team_owners:
|
||||
owners = [user_id]
|
||||
if not owners and team_owners:
|
||||
owners = []
|
||||
if not users:
|
||||
users = []
|
||||
if not team_owners:
|
||||
team_owners = []
|
||||
if not team_users:
|
||||
team_users = []
|
||||
|
||||
created_worklist = worklists_api.create(worklist_dict)
|
||||
events_api.worklist_created_event(created_worklist.id,
|
||||
@ -759,12 +781,14 @@ class WorklistsController(rest.RestController):
|
||||
edit_permission = {
|
||||
'name': 'edit_worklist_%d' % created_worklist.id,
|
||||
'codename': 'edit_worklist',
|
||||
'users': owners
|
||||
'users': owners,
|
||||
'teams': team_owners
|
||||
}
|
||||
move_permission = {
|
||||
'name': 'move_items_%d' % created_worklist.id,
|
||||
'codename': 'move_items',
|
||||
'users': users
|
||||
'users': users,
|
||||
'teams': team_users
|
||||
}
|
||||
edit = worklists_api.create_permission(
|
||||
created_worklist.id, edit_permission)
|
||||
@ -773,19 +797,24 @@ class WorklistsController(rest.RestController):
|
||||
|
||||
event_owners = [{id: users_api.user_get(id).full_name}
|
||||
for id in owners]
|
||||
event_team_owners = [{id: teams_api.team_get(id).name}
|
||||
for id in owners]
|
||||
event_users = [{id: users_api.user_get(id).full_name}
|
||||
for id in users]
|
||||
|
||||
event_team_users = [{id: teams_api.team_get(id).name}
|
||||
for id in users]
|
||||
events_api.worklist_permission_created_event(created_worklist.id,
|
||||
user_id,
|
||||
edit.id,
|
||||
edit.codename,
|
||||
event_owners)
|
||||
event_owners,
|
||||
event_team_owners)
|
||||
events_api.worklist_permission_created_event(created_worklist.id,
|
||||
user_id,
|
||||
move.id,
|
||||
move.codename,
|
||||
event_users)
|
||||
event_users,
|
||||
event_team_users)
|
||||
|
||||
if worklist_dict['automatic']:
|
||||
for filter in filters:
|
||||
|
@ -464,6 +464,13 @@ def filter_private_worklists(query, current_user, hide_lanes=True):
|
||||
models.User.id == current_user
|
||||
)
|
||||
),
|
||||
models.Board.permissions.any(
|
||||
models.Permission.teams.any(
|
||||
models.Team.users.any(
|
||||
models.User.id == current_user
|
||||
)
|
||||
)
|
||||
),
|
||||
models.Board.private == false(),
|
||||
models.Board.id.is_(None)
|
||||
)
|
||||
@ -476,6 +483,13 @@ def filter_private_worklists(query, current_user, hide_lanes=True):
|
||||
models.User.id == current_user
|
||||
)
|
||||
),
|
||||
models.Worklist.permissions.any(
|
||||
models.Permission.teams.any(
|
||||
models.Team.users.any(
|
||||
models.User.id == current_user
|
||||
)
|
||||
)
|
||||
),
|
||||
models.Worklist.private == false(),
|
||||
models.Worklist.id.is_(None)
|
||||
)
|
||||
@ -519,6 +533,13 @@ def filter_private_boards(query, current_user):
|
||||
models.User.id == current_user
|
||||
)
|
||||
),
|
||||
models.Board.permissions.any(
|
||||
models.Permission.teams.any(
|
||||
models.Team.users.any(
|
||||
models.User.id == current_user
|
||||
)
|
||||
)
|
||||
),
|
||||
models.Board.private == false(),
|
||||
models.Board.id.is_(None)
|
||||
)
|
||||
|
@ -18,6 +18,7 @@ from wsme.exc import ClientSideError
|
||||
|
||||
from storyboard._i18n import _
|
||||
from storyboard.db.api import base as api_base
|
||||
from storyboard.db.api import teams as teams_api
|
||||
from storyboard.db.api import users as users_api
|
||||
from storyboard.db import models
|
||||
|
||||
@ -212,35 +213,54 @@ def has_card(board, item_type, item_id):
|
||||
return False
|
||||
|
||||
|
||||
def get_owners(board):
|
||||
def get_user_owners(board):
|
||||
for permission in board.permissions:
|
||||
if permission.codename == 'edit_board':
|
||||
return [user.id for user in permission.users]
|
||||
|
||||
|
||||
def get_users(board):
|
||||
def get_user_users(board):
|
||||
for permission in board.permissions:
|
||||
if permission.codename == 'move_cards':
|
||||
return [user.id for user in permission.users]
|
||||
|
||||
|
||||
def get_team_owners(board):
|
||||
for permission in board.permissions:
|
||||
if permission.codename == 'edit_board':
|
||||
return [team.id for team in permission.teams]
|
||||
|
||||
|
||||
def get_team_users(board):
|
||||
for permission in board.permissions:
|
||||
if permission.codename == 'move_cards':
|
||||
return [team.id for team in permission.teams]
|
||||
|
||||
|
||||
def get_permissions(board, user_id):
|
||||
user = users_api.user_get(user_id)
|
||||
if user is not None:
|
||||
valid_permissions = set(user.permissions)
|
||||
for team in user.teams:
|
||||
valid_permissions.update(team.permissions)
|
||||
return [permission.codename for permission in board.permissions
|
||||
if permission in user.permissions]
|
||||
if permission in valid_permissions]
|
||||
return []
|
||||
|
||||
|
||||
def create_permission(board_id, permission_dict, session=None):
|
||||
board = _board_get(board_id, session=session)
|
||||
users = permission_dict.pop('users')
|
||||
teams = permission_dict.pop('teams')
|
||||
permission = api_base.entity_create(
|
||||
models.Permission, permission_dict, session=session)
|
||||
board.permissions.append(permission)
|
||||
for user_id in users:
|
||||
user = users_api.user_get(user_id, session=session)
|
||||
user.permissions.append(permission)
|
||||
for team_id in teams:
|
||||
team = teams_api.team_get(team_id, session=session)
|
||||
team.permissions.append(permission)
|
||||
return permission
|
||||
|
||||
|
||||
@ -250,11 +270,14 @@ def update_permission(board_id, permission_dict):
|
||||
for permission in board.permissions:
|
||||
if permission.codename == permission_dict['codename']:
|
||||
id = permission.id
|
||||
users = permission_dict.pop('users')
|
||||
permission_dict['users'] = []
|
||||
for user_id in users:
|
||||
user = users_api.user_get(user_id)
|
||||
permission_dict['users'].append(user)
|
||||
users = permission_dict.pop('users', None)
|
||||
teams = permission_dict.pop('teams', None)
|
||||
if users is not None:
|
||||
permission_dict['users'] = [users_api.user_get(user_id)
|
||||
for user_id in users]
|
||||
if teams is not None:
|
||||
permission_dict['teams'] = [teams_api.team_get(team_id)
|
||||
for team_id in teams]
|
||||
|
||||
if id is None:
|
||||
raise ClientSideError(_("Permission %s does not exist")
|
||||
|
@ -339,13 +339,14 @@ def worklist_details_changed_event(worklist_id, author_id, updated, old, new):
|
||||
|
||||
|
||||
def worklist_permission_created_event(worklist_id, author_id, permission_id,
|
||||
codename, users):
|
||||
codename, users, teams):
|
||||
event_info = {
|
||||
"worklist_id": worklist_id,
|
||||
"permission_id": permission_id,
|
||||
"author_id": author_id,
|
||||
"codename": codename,
|
||||
"users": users
|
||||
"users": users,
|
||||
"teams": teams
|
||||
}
|
||||
|
||||
return event_create({
|
||||
@ -445,13 +446,14 @@ def board_details_changed_event(board_id, author_id, updated, old, new):
|
||||
|
||||
|
||||
def board_permission_created_event(board_id, author_id, permission_id,
|
||||
codename, users):
|
||||
codename, users, teams):
|
||||
event_info = {
|
||||
"board_id": board_id,
|
||||
"permission_id": permission_id,
|
||||
"author_id": author_id,
|
||||
"codename": codename,
|
||||
"users": users
|
||||
"users": users,
|
||||
"teams": teams
|
||||
}
|
||||
|
||||
return event_create({
|
||||
|
@ -23,6 +23,7 @@ from storyboard.db.api import base as api_base
|
||||
from storyboard.db.api import boards
|
||||
from storyboard.db.api import stories as stories_api
|
||||
from storyboard.db.api import tasks as tasks_api
|
||||
from storyboard.db.api import teams as teams_api
|
||||
from storyboard.db.api import users as users_api
|
||||
from storyboard.db import models
|
||||
|
||||
@ -365,35 +366,54 @@ def is_lane(worklist):
|
||||
return False
|
||||
|
||||
|
||||
def get_owners(worklist):
|
||||
def get_user_owners(worklist):
|
||||
for permission in worklist.permissions:
|
||||
if permission.codename == 'edit_worklist':
|
||||
return [user.id for user in permission.users]
|
||||
|
||||
|
||||
def get_users(worklist):
|
||||
def get_user_users(worklist):
|
||||
for permission in worklist.permissions:
|
||||
if permission.codename == 'move_items':
|
||||
return [user.id for user in permission.users]
|
||||
|
||||
|
||||
def get_team_owners(worklist):
|
||||
for permission in worklist.permissions:
|
||||
if permission.codename == 'edit_worklist':
|
||||
return [team.id for team in permission.teams]
|
||||
|
||||
|
||||
def get_team_users(worklist):
|
||||
for permission in worklist.permissions:
|
||||
if permission.codename == 'move_items':
|
||||
return [team.id for team in permission.teams]
|
||||
|
||||
|
||||
def get_permissions(worklist, user_id):
|
||||
user = users_api.user_get(user_id)
|
||||
if user is not None:
|
||||
valid_permissions = set(user.permissions)
|
||||
for team in user.teams:
|
||||
valid_permissions.update(team.permissions)
|
||||
return [permission.codename for permission in worklist.permissions
|
||||
if permission in user.permissions]
|
||||
if permission in valid_permissions]
|
||||
return []
|
||||
|
||||
|
||||
def create_permission(worklist_id, permission_dict, session=None):
|
||||
worklist = _worklist_get(worklist_id, session=session)
|
||||
users = permission_dict.pop('users')
|
||||
teams = permission_dict.pop('teams')
|
||||
permission = api_base.entity_create(
|
||||
models.Permission, permission_dict, session=session)
|
||||
worklist.permissions.append(permission)
|
||||
for user_id in users:
|
||||
user = users_api.user_get(user_id, session=session)
|
||||
user.permissions.append(permission)
|
||||
for team_id in teams:
|
||||
team = teams_api.team_get(team_id, session=session)
|
||||
team.permissions.append(permission)
|
||||
return permission
|
||||
|
||||
|
||||
@ -403,11 +423,14 @@ def update_permission(worklist_id, permission_dict):
|
||||
for permission in worklist.permissions:
|
||||
if permission.codename == permission_dict['codename']:
|
||||
id = permission.id
|
||||
users = permission_dict.pop('users')
|
||||
permission_dict['users'] = []
|
||||
for user_id in users:
|
||||
user = users_api.user_get(user_id)
|
||||
permission_dict['users'].append(user)
|
||||
users = permission_dict.pop('users', None)
|
||||
teams = permission_dict.pop('teams', None)
|
||||
if users is not None:
|
||||
permission_dict['users'] = [users_api.user_get(user_id)
|
||||
for user_id in users]
|
||||
if teams is not None:
|
||||
permission_dict['teams'] = [teams_api.team_get(team_id)
|
||||
for team_id in teams]
|
||||
|
||||
if id is None:
|
||||
raise ClientSideError(_("Permission %s does not exist")
|
||||
|
@ -635,7 +635,8 @@ class Board(FullText, ModelBuilder, Base):
|
||||
private = Column(Boolean, default=False)
|
||||
archived = Column(Boolean, default=False)
|
||||
lanes = relationship(BoardWorklist, backref='board')
|
||||
permissions = relationship("Permission", secondary="board_permissions")
|
||||
permissions = relationship("Permission", secondary="board_permissions",
|
||||
backref="boards")
|
||||
|
||||
_public_fields = ["id", "title", "description", "creator_id",
|
||||
"project_id", "permission_id", "private", "archived"]
|
||||
|
Loading…
x
Reference in New Issue
Block a user