Skip to content
Merged
Show file tree
Hide file tree
Changes from 49 commits
Commits
Show all changes
51 commits
Select commit Hold shift + click to select a range
306220d
Implementation of RBAC for KeyValuePair
ashwini-orchestral Sep 9, 2021
09a0218
Resolved formatting issue
ashwini-orchestral Sep 9, 2021
0627467
Added a comment
ashwini-orchestral Sep 9, 2021
ed9c0d5
Modifications done as per review comments
ashwini-orchestral Sep 16, 2021
242f230
Fixed review changes
ashwini-orchestral Sep 17, 2021
bd1adf3
Minor fix
ashwini-orchestral Sep 17, 2021
1ae5b94
Merge branch 'master' into rbac_keyvaluepair
ashwini-orchestral Sep 17, 2021
faeb378
Modified the implementation for default user
ashwini-orchestral Sep 22, 2021
7bc6613
Merge branch 'master' into rbac_keyvaluepair
ashwini-orchestral Sep 23, 2021
2f17754
Fixed lint error
ashwini-orchestral Sep 23, 2021
1c77ff7
Minor fix to handle system key permissions to user.
ashwini-orchestral Sep 23, 2021
a2e7178
Merge branch 'master' into rbac_keyvaluepair
ashwini-orchestral Sep 24, 2021
1a871d0
Added a function to get the list keys of user for system scoped kvps
ashwini-orchestral Sep 24, 2021
57a7808
Modified get_all function to get the list of user assigned system sco…
ashwini-orchestral Sep 24, 2021
4dedb78
Minor fix to add imports
ashwini-orchestral Sep 24, 2021
691c7aa
Minor fix to add import
ashwini-orchestral Sep 24, 2021
da1d6bd
Minor fix related to lint error
ashwini-orchestral Sep 24, 2021
b39855d
Minor fix related to conflict
ashwini-orchestral Sep 24, 2021
1d62db8
Reformatted file with black tool
ashwini-orchestral Sep 24, 2021
f244569
Minor fix for users system scope key list
ashwini-orchestral Sep 27, 2021
5270fcb
Added testcases for user permission with system scope kvps.
ashwini-orchestral Sep 27, 2021
7f7635f
Fixed lint error
ashwini-orchestral Sep 27, 2021
eef5655
Fixed minor variable issue
ashwini-orchestral Sep 27, 2021
092723a
Added unit tests for user permission
ashwini-orchestral Sep 27, 2021
b63319c
Minor fix related to lint error
ashwini-orchestral Sep 27, 2021
88db871
Minor fix related to import
ashwini-orchestral Sep 27, 2021
eab3555
Refactor KeyValuePair get_all method in API controller
m4dcoder Sep 30, 2021
07ac6f8
Refactor the get_one, put, delete for KVP
ashwini-orchestral Sep 30, 2021
7544f95
Modified unit tests to add multiple users and roles for KVP
ashwini-orchestral Sep 30, 2021
c04b7a5
Modified method to return only keyvaluepair keys
ashwini-orchestral Sep 30, 2021
0881982
Modified unit tests for multiple users and roles kvp
ashwini-orchestral Sep 30, 2021
6a9e1e4
Fixed lint error
ashwini-orchestral Sep 30, 2021
3649ef9
Fixed lint error
ashwini-orchestral Sep 30, 2021
c7250f7
Fixed lint error
ashwini-orchestral Sep 30, 2021
6c12ef7
Rollback changes to get_all for key value API
m4dcoder Sep 30, 2021
6522420
Refactor and simplify get_one method of key value API
m4dcoder Oct 1, 2021
ea5bf2e
Fix kvp unit tests to be more dynamic in asserting list
m4dcoder Oct 1, 2021
fafe328
Add observer support to get_all for key value API
m4dcoder Oct 2, 2021
0dbae40
Refactor and simplify put method of key value API
m4dcoder Oct 3, 2021
f9b2c3c
Minor black formatting changes
m4dcoder Oct 3, 2021
77632eb
Refactor and simplify delete method of key value API
m4dcoder Oct 3, 2021
9189455
Refactor get_all_system_kvp_names_for_user with more restriction
m4dcoder Oct 3, 2021
66989cc
Refactor RBAC unit tests for the key value API
m4dcoder Oct 3, 2021
fa38953
Merge remote-tracking branch 'origin' into rbac_keyvaluepair
m4dcoder Oct 3, 2021
ec3a184
Fixed minor issue of CLI command
ashwini-orchestral Oct 4, 2021
b9309e5
Removed commented line
ashwini-orchestral Oct 5, 2021
51b580c
Merge branch 'master' into rbac_keyvaluepair
ashwini-orchestral Oct 5, 2021
a2e51f4
Merge branch 'master' into rbac_keyvaluepair
ashwini-orchestral Oct 28, 2021
816e6ba
Merge branch 'master' into rbac_keyvaluepair
m4dcoder Dec 9, 2021
1747eee
Merge branch 'master' into rbac_keyvaluepair
ashwini-orchestral Dec 10, 2021
d32a7f3
Updated changelog for RBAC keyvaluepair
ashwini-orchestral Dec 10, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
313 changes: 162 additions & 151 deletions st2api/st2api/controllers/v1/keyvalue.py

Large diffs are not rendered by default.

323 changes: 248 additions & 75 deletions st2api/tests/unit/controllers/v1/test_kvps.py

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions st2client/st2client/commands/resource.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,6 @@
import six
import json
import logging
import traceback

from functools import wraps

Expand Down Expand Up @@ -200,10 +199,11 @@ def get_resource_by_pk(self, pk, **kwargs):
try:
instance = self.manager.get_by_id(pk, **kwargs)
except Exception as e:
traceback.print_exc()
# Hack for "Unauthorized" exceptions, we do want to propagate those
response = getattr(e, "response", None)
status_code = getattr(response, "status_code", None)
if status_code and status_code == http_client.FORBIDDEN:
raise e
if status_code and status_code == http_client.UNAUTHORIZED:
raise e

Expand Down
10 changes: 10 additions & 0 deletions st2common/st2common/rbac/backends/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -229,6 +229,16 @@ def user_has_role(user_db, role):
"""
raise NotImplementedError()

@staticmethod
def user_has_system_role(role):
"""
:param user: User object to check for.
:type user: :class:`UserDB`

:rtype: ``bool``
"""
raise NotImplementedError()

@staticmethod
def user_has_rule_trigger_permission(user_db, trigger):
"""
Expand Down
10 changes: 10 additions & 0 deletions st2common/st2common/rbac/backends/noop.py
Original file line number Diff line number Diff line change
Expand Up @@ -195,6 +195,16 @@ def user_has_role(user_db, role):
"""
return True

@staticmethod
def user_has_system_role(user_db):
"""
:param user: User object to check for.
:type user: :class:`UserDB`

:rtype: ``bool``
"""
return True

@staticmethod
def user_has_rule_trigger_permission(user_db, trigger):
"""
Expand Down
42 changes: 41 additions & 1 deletion st2common/st2common/services/keyvalues.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,11 +21,15 @@
from st2common.constants.keyvalue import SYSTEM_SCOPE, FULL_SYSTEM_SCOPE
from st2common.constants.keyvalue import USER_SCOPE, FULL_USER_SCOPE
from st2common.constants.keyvalue import ALLOWED_SCOPES
from st2common.constants.keyvalue import DATASTORE_KEY_SEPARATOR
from st2common.constants.keyvalue import DATASTORE_KEY_SEPARATOR, USER_SEPARATOR
from st2common.exceptions.db import StackStormDBObjectNotFoundError
from st2common.exceptions.keyvalue import InvalidScopeException, InvalidUserException
from st2common.models.system.keyvalue import UserKeyReference
from st2common.persistence.keyvalue import KeyValuePair
from st2common.persistence.rbac import UserRoleAssignment
from st2common.persistence.rbac import Role
from st2common.persistence.rbac import PermissionGrant
from st2common.constants.types import ResourceType

__all__ = [
"get_kvp_for_name",
Expand Down Expand Up @@ -256,3 +260,39 @@ def get_key_reference(scope, name, user=None):
raise InvalidScopeException(
'Scope "%s" is not valid. Allowed scopes are %s.' % (scope, ALLOWED_SCOPES)
)


def get_key_uids_for_user(user):
role_names = UserRoleAssignment.query(user=user).only("role").scalar("role")
permission_grant_ids = Role.query(name__in=role_names).scalar("permission_grants")
permission_grant_ids = sum(permission_grant_ids, [])
permission_grants_filters = {}
permission_grants_filters["id__in"] = permission_grant_ids
permission_grants_filters["resource_type"] = ResourceType.KEY_VALUE_PAIR
return PermissionGrant.query(**permission_grants_filters).scalar("resource_uid")


def get_all_system_kvp_names_for_user(user):
"""
Retrieve all the permission grants for a particular user.
The result will return the key list

:rtype: ``list``
"""
key_list = []

for uid in get_key_uids_for_user(user):
pfx = "%s%s%s" % (
ResourceType.KEY_VALUE_PAIR,
DATASTORE_KEY_SEPARATOR,
FULL_SYSTEM_SCOPE,
)
if not uid.startswith(pfx):
continue

key_name = uid.split(DATASTORE_KEY_SEPARATOR)[2:]

if key_name and key_name not in key_list:
key_list.append(USER_SEPARATOR.join(key_name))

return sorted(key_list)
129 changes: 126 additions & 3 deletions st2common/tests/unit/services/test_keyvalue.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,14 +14,26 @@
# limitations under the License.

from __future__ import absolute_import
import unittest2
from st2tests.api import FunctionalTest

from st2common.constants.keyvalue import SYSTEM_SCOPE, USER_SCOPE
from st2common.constants.keyvalue import SYSTEM_SCOPE, FULL_SYSTEM_SCOPE
from st2common.constants.keyvalue import USER_SCOPE, FULL_USER_SCOPE
from st2common.exceptions.keyvalue import InvalidScopeException, InvalidUserException
from st2common.services.keyvalues import get_key_reference
from st2common.services.keyvalues import get_all_system_kvp_names_for_user
from st2common.persistence.auth import User
from st2common.models.db.auth import UserDB
from st2common.models.db.rbac import UserRoleAssignmentDB
from st2common.models.db.rbac import PermissionGrantDB
from st2common.rbac.types import PermissionType
from st2common.rbac.types import ResourceType
from st2common.persistence.rbac import UserRoleAssignment
from st2common.persistence.rbac import PermissionGrant
from st2common.persistence.rbac import Role
from st2common.models.db.rbac import RoleDB


class KeyValueServicesTest(unittest2.TestCase):
class KeyValueServicesTest(FunctionalTest):
def test_get_key_reference_system_scope(self):
ref = get_key_reference(scope=SYSTEM_SCOPE, name="foo")
self.assertEqual(ref, "foo")
Expand All @@ -41,3 +53,114 @@ def test_get_key_reference_invalid_scope_raises_exception(self):
self.assertRaises(
InvalidScopeException, get_key_reference, scope="sketchy", name="foo"
)

def test_get_all_system_kvp_names_for_user(self):
user1, user2 = "user1", "user2"
kvp_1_uid = "%s:%s:s101" % (ResourceType.KEY_VALUE_PAIR, FULL_SYSTEM_SCOPE)
kvp_2_uid = "%s:%s:s102" % (ResourceType.KEY_VALUE_PAIR, FULL_SYSTEM_SCOPE)
kvp_3_uid = "%s:%s:%s:u101" % (
ResourceType.KEY_VALUE_PAIR,
FULL_USER_SCOPE,
user1,
)
kvp_4_uid = "%s:%s:echo" % (ResourceType.ACTION, "core")
kvp_5_uid = "%s:%s:new_action" % (ResourceType.ACTION, "dummy")
kvp_6_uid = "%s:%s:s103" % (ResourceType.KEY_VALUE_PAIR, FULL_SYSTEM_SCOPE)

# Setup user1, grant, role, and assignment records
user_1_db = UserDB(name=user1)
user_1_db = User.add_or_update(user_1_db)

grant_1_db = PermissionGrantDB(
resource_uid=kvp_1_uid,
resource_type=ResourceType.KEY_VALUE_PAIR,
permission_types=[PermissionType.KEY_VALUE_PAIR_LIST],
)
grant_1_db = PermissionGrant.add_or_update(grant_1_db)

grant_2_db = PermissionGrantDB(
resource_uid=kvp_2_uid,
resource_type=ResourceType.KEY_VALUE_PAIR,
permission_types=[PermissionType.KEY_VALUE_PAIR_VIEW],
)
grant_2_db = PermissionGrant.add_or_update(grant_2_db)

grant_3_db = PermissionGrantDB(
resource_uid=kvp_3_uid,
resource_type=ResourceType.KEY_VALUE_PAIR,
permission_types=[PermissionType.KEY_VALUE_PAIR_ALL],
)
grant_3_db = PermissionGrant.add_or_update(grant_3_db)

grant_4_db = PermissionGrantDB(
resource_uid=kvp_4_uid,
resource_type=ResourceType.ACTION,
permission_types=[PermissionType.ACTION_VIEW],
)
grant_4_db = PermissionGrant.add_or_update(grant_4_db)

grant_5_db = PermissionGrantDB(
resource_uid=kvp_5_uid,
resource_type=ResourceType.ACTION,
permission_types=[PermissionType.ACTION_LIST],
)
grant_5_db = PermissionGrant.add_or_update(grant_5_db)

role_1_db = RoleDB(
name="user1_custom_role_grant",
permission_grants=[
str(grant_1_db.id),
str(grant_2_db.id),
str(grant_3_db.id),
str(grant_4_db.id),
],
)
role_1_db = Role.add_or_update(role_1_db)

role_1_assignment_db = UserRoleAssignmentDB(
user=user_1_db.name,
role=role_1_db.name,
source="assignments/%s.yaml" % user_1_db.name,
)
UserRoleAssignment.add_or_update(role_1_assignment_db)

# Setup user2, grant, role, and assignment records
user_2_db = UserDB(name=user2)
user_2_db = User.add_or_update(user_2_db)

grant_6_db = PermissionGrantDB(
resource_uid=kvp_6_uid,
resource_type=ResourceType.KEY_VALUE_PAIR,
permission_types=[PermissionType.KEY_VALUE_PAIR_ALL],
)
grant_6_db = PermissionGrant.add_or_update(grant_6_db)

role_2_db = RoleDB(
name="user2_custom_role_grant",
permission_grants=[
str(grant_5_db.id),
str(grant_6_db.id),
],
)
role_2_db = Role.add_or_update(role_2_db)

role_2_assignment_db = UserRoleAssignmentDB(
user=user_2_db.name,
role=role_2_db.name,
source="assignments/%s.yaml" % user_2_db.name,
)
UserRoleAssignment.add_or_update(role_2_assignment_db)

# Assert result of get_all_system_kvp_names_for_user for user1
# The uids for non key value pair resource type should not be included in the result.
# The user scoped key should not be included in the result.
actual_result = get_all_system_kvp_names_for_user(user=user_1_db.name)
expected_result = ["s101", "s102"]
self.assertListEqual(actual_result, expected_result)

# Assert result of get_all_system_kvp_names_for_user for user2
# The uids for non key value pair resource type should not be included in the result.
# The user scoped key should not be included in the result.
actual_result = get_all_system_kvp_names_for_user(user=user_2_db.name)
expected_result = ["s103"]
self.assertListEqual(actual_result, expected_result)