Merge "Allow permissions to be set for teams in worklists and boards"

This commit is contained in:
Zuul 2019-06-09 15:42:47 +00:00 committed by Gerrit Code Review
commit cb42ebd1cb
8 changed files with 183 additions and 39 deletions

View File

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

View File

@ -801,6 +801,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."""
@ -857,8 +863,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):
@ -955,6 +963,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(
@ -982,7 +996,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={},
@ -1011,5 +1027,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)

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -643,7 +643,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"]