def test_is_admin(self): # Make sure RBAC is enabled for the tests cfg.CONF.set_override(name='enable', override=True, group='rbac') # Admin user self.assertTrue(user_is_admin(user_db=self.admin_user)) # Regular user self.assertFalse(user_is_admin(user_db=self.regular_user))
def get(self, requester_user, auth_info): """ Meta API endpoint wich returns information about the currently authenticated user. Handle: GET /v1/user """ data = {} if cfg.CONF.rbac.enable and requester_user: role_dbs = get_roles_for_user(user_db=requester_user) roles = [role_db.name for role_db in role_dbs] else: roles = [] data = { 'username': requester_user.name, 'authentication': { 'method': auth_info['method'], 'location': auth_info['location'] }, 'rbac': { 'enabled': cfg.CONF.rbac.enable, 'roles': roles, 'is_admin': rbac_utils.user_is_admin(user_db=requester_user) } } if auth_info.get('token_expire', None): token_expire = auth_info['token_expire'].strftime( '%Y-%m-%dT%H:%M:%SZ') data['authentication']['token_expire'] = token_expire return data
def get_key(key=None, user=None, scope=None, decrypt=False): """Retrieve key from KVP store """ if not isinstance(key, six.string_types): raise TypeError('Given key is not typeof string.') if not isinstance(decrypt, bool): raise TypeError('Decrypt parameter is not typeof bool.') if not user: user = UserDB(cfg.CONF.system_user.user) scope, key_id = _derive_scope_and_key(key, user, scope) scope = get_datastore_full_scope(scope) LOG.debug('get_key scope: %s', scope) _validate_scope(scope=scope) is_admin = rbac_utils.user_is_admin(user_db=user) # User needs to be either admin or requesting item for itself _validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, user=user) value = KeyValuePair.get_by_scope_and_name(scope, key_id) if value: return deserialize_key_value(value.value, decrypt) return None
def test_feature_flag_returns_false_on_rbac_enabled(self): cfg.CONF.set_override(name="enable", override=True, group="rbac") # TODO: Enable once checks are implemented return result = utils.user_is_admin(user_db=self.mocks["user_db"]) self.assertFalse(result)
def get(self, requester_user, auth_info): """ Meta API endpoint wich returns information about the currently authenticated user. Handle: GET /v1/user """ data = {} if cfg.CONF.rbac.enable and requester_user: role_dbs = get_roles_for_user(user_db=requester_user) roles = [role_db.name for role_db in role_dbs] else: roles = [] data = { 'username': requester_user.name, 'authentication': { 'method': auth_info['method'], 'location': auth_info['location'] }, 'rbac': { 'enabled': cfg.CONF.rbac.enable, 'roles': roles, 'is_admin': rbac_utils.user_is_admin(user_db=requester_user) } } if auth_info.get('token_expire', None): token_expire = auth_info['token_expire'].strftime('%Y-%m-%dT%H:%M:%SZ') data['authentication']['token_expire'] = token_expire return data
def test_feature_flag_returns_false_on_rbac_enabled(self): cfg.CONF.set_override(name='enable', override=True, group='rbac') # TODO: Enable once checks are implemented return result = utils.user_is_admin(user_db=self.mocks['user_db']) self.assertFalse(result)
def get_all(self, requester_user, prefix=None, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False, sort=None, offset=0, limit=None, **raw_filters): """ List all keys. Handles requests: GET /keys/ """ if not scope: scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) is_all_scope = (scope == ALL_SCOPE) is_admin = rbac_utils.user_is_admin(user_db=requester_user) if is_all_scope and not is_admin: msg = '"all" scope requires administrator access' raise AccessDeniedError(message=msg, user_db=requester_user) # User needs to be either admin or requesting items for themselves self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, requester_user=requester_user) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) from_model_kwargs = {'mask_secrets': not decrypt} if scope and scope not in ALL_SCOPE: self._validate_scope(scope=scope) raw_filters['scope'] = scope if scope == USER_SCOPE or scope == FULL_USER_SCOPE: # Make sure we only returned values scoped to current user if prefix: prefix = get_key_reference(name=prefix, scope=scope, user=user) else: prefix = get_key_reference(name='', scope=scope, user=user) raw_filters['prefix'] = prefix kvp_apis = super(KeyValuePairController, self)._get_all(from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user) return kvp_apis
def get_all(self, requester_user, prefix=None, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False, sort=None, offset=0, limit=None, **raw_filters): """ List all keys. Handles requests: GET /keys/ """ if not scope: scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) is_all_scope = (scope == ALL_SCOPE) is_admin = rbac_utils.user_is_admin(user_db=requester_user) if is_all_scope and not is_admin: msg = '"all" scope requires administrator access' raise AccessDeniedError(message=msg, user_db=requester_user) # User needs to be either admin or requesting items for themselves self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, requester_user=requester_user) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) from_model_kwargs = {'mask_secrets': not decrypt} if scope and scope not in ALL_SCOPE: self._validate_scope(scope=scope) raw_filters['scope'] = scope if scope == USER_SCOPE or scope == FULL_USER_SCOPE: # Make sure we only returned values scoped to current user if prefix: prefix = get_key_reference(name=prefix, scope=scope, user=user) else: prefix = get_key_reference(name='', scope=scope, user=user) raw_filters['prefix'] = prefix kvp_apis = super(KeyValuePairController, self)._get_all(from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters) return kvp_apis
def _validate_all_scope(self, scope, requester_user): """ Validate that "all" scope can only be provided by admins on RBAC installations. """ scope = get_datastore_full_scope(scope) is_all_scope = (scope == ALL_SCOPE) is_admin = rbac_utils.user_is_admin(user_db=requester_user) if is_all_scope and not is_admin: msg = '"all" scope requires administrator access' raise AccessDeniedError(message=msg, user_db=requester_user)
def _validate_decrypt_query_parameter(self, decrypt, scope, requester_user): """ Validate that the provider user is either admin or requesting to decrypt value for themselves. """ is_admin = rbac_utils.user_is_admin(user_db=requester_user) is_user_scope = (scope == USER_SCOPE or scope == FULL_USER_SCOPE) if decrypt and (not is_user_scope and not is_admin): msg = 'Decrypt option requires administrator access' raise AccessDeniedError(message=msg, user_db=requester_user)
def _get_mask_secrets(self, requester_user, show_secrets=None): """ Return a value for mask_secrets which can be used in masking secret properties to be retruned by any API. The default value is as per the config however admin users have the ability to override by passing in a special query parameter ?show_secrets=True. :rtype: ``bool`` """ mask_secrets = cfg.CONF.api.mask_secrets if show_secrets and rbac_utils.user_is_admin(user_db=requester_user): mask_secrets = False return mask_secrets
def get_one(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False): """ List key by name. Handle: GET /keys/key1 """ if not scope: scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) is_admin = rbac_utils.user_is_admin(user_db=requester_user) # User needs to be either admin or requesting item for itself self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, requester_user=requester_user) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided( user_db=requester_user, user=user) key_ref = get_key_reference(scope=scope, name=name, user=user) from_model_kwargs = {'mask_secrets': not decrypt} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs) return kvp_api
def get_one(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False): """ List key by name. Handle: GET /keys/key1 """ if not scope: scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) is_admin = rbac_utils.user_is_admin(user_db=requester_user) # User needs to be either admin or requesting item for itself self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, is_admin=is_admin, requester_user=requester_user) user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user) key_ref = get_key_reference(scope=scope, name=name, user=user) from_model_kwargs = {'mask_secrets': not decrypt} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs ) return kvp_api
def resource_model_filter(self, model, instance, requester_user=None, **from_model_kwargs): # RBAC or permission isolation is disabled, bail out if not (cfg.CONF.rbac.enable and cfg.CONF.rbac.permission_isolation): result = super(BaseResourceIsolationControllerMixin, self).resource_model_filter( model=model, instance=instance, requester_user=requester_user, **from_model_kwargs) return result user_is_admin = rbac_utils.user_is_admin(user_db=requester_user) user_is_system_user = (requester_user.name == cfg.CONF.system_user.user) item = model.from_model(instance, **from_model_kwargs) # Admin users and system users can view all the resoruces if user_is_admin or user_is_system_user: return item user = item.context.get('user', None) if user and (user == requester_user.name): return item return None
def validate_limit_query_param(limit, requester_user=None): """ Validate that the provided value for "limit" query parameter is valid. Note: We only perform max_page_size check for non-admin users. Admin users can provide arbitrary limit value. """ user_is_admin = rbac_utils.user_is_admin(user_db=requester_user) if limit: # Display all the results if int(limit) == -1: if not user_is_admin: # Only admins can specify limit -1 message = ( 'Administrator access required to be able to specify limit=-1 and ' 'retrieve all the records') raise AccessDeniedError(message=message, user_db=requester_user) return 0 elif int(limit) <= -2: msg = 'Limit, "%s" specified, must be a positive number.' % (limit) raise ValueError(msg) elif int(limit) > cfg.CONF.api.max_page_size and not user_is_admin: msg = ('Limit "%s" specified, maximum value is "%s"' % (limit, cfg.CONF.api.max_page_size)) raise AccessDeniedError(message=msg, user_db=requester_user) # Disable n = 0 elif limit == 0: msg = ( 'Limit, "%s" specified, must be a positive number or -1 for full result set.' % (limit)) raise ValueError(msg) return limit
def validate_limit_query_param(limit, requester_user=None): """ Validate that the provided value for "limit" query parameter is valid. Note: We only perform max_page_size check for non-admin users. Admin users can provide arbitrary limit value. """ user_is_admin = rbac_utils.user_is_admin(user_db=requester_user) if limit: # Display all the results if int(limit) == -1: if not user_is_admin: # Only admins can specify limit -1 message = ('Administrator access required to be able to specify limit=-1 and ' 'retrieve all the records') raise AccessDeniedError(message=message, user_db=requester_user) return 0 elif int(limit) <= -2: msg = 'Limit, "%s" specified, must be a positive number.' % (limit) raise ValueError(msg) elif int(limit) > cfg.CONF.api.max_page_size and not user_is_admin: msg = ('Limit "%s" specified, maximum value is "%s"' % (limit, cfg.CONF.api.max_page_size)) raise AccessDeniedError(message=msg, user_db=requester_user) # Disable n = 0 elif limit == 0: msg = ('Limit, "%s" specified, must be a positive number or -1 for full result set.' % (limit)) raise ValueError(msg) return limit
def test_is_admin(self): # Admin user self.assertTrue(user_is_admin(user=self.admin_user)) # Regular user self.assertFalse(user_is_admin(user=self.regular_user))
def test_feature_flag_returns_true_on_rbac_disabled(self): # When feature RBAC is disabled, all the functions should return True cfg.CONF.set_override(name="enable", override=False, group="rbac") result = utils.user_is_admin(user_db=self.mocks["user_db"]) self.assertTrue(result)
def get_one(self, name, requester_user, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False): """ List key by name. Handle: GET /keys/key1 """ if not scope: # Default to system scope scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) self._validate_scope(scope=scope) # User needs to be either admin or requesting item for itself self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, requester_user=requester_user) user_query_param_filter = bool(user) current_user = requester_user.name user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user, require_rbac=True) # Additional guard to ensure there is no information leakage across users is_admin = rbac_utils.user_is_admin(user_db=requester_user) if is_admin and user_query_param_filter: # Retrieve values scoped to the provided user user_scope_prefix = get_key_reference(name=name, scope=USER_SCOPE, user=user) else: # RBAC not enabled or user is not an admin, retrieve user scoped values for the # current user user_scope_prefix = get_key_reference(name=name, scope=USER_SCOPE, user=current_user) if scope == FULL_USER_SCOPE: key_ref = user_scope_prefix elif scope == FULL_SYSTEM_SCOPE: key_ref = get_key_reference(scope=FULL_SYSTEM_SCOPE, name=name, user=user) else: raise ValueError('Invalid scope: %s' % (scope)) from_model_kwargs = {'mask_secrets': not decrypt} kvp_api = self._get_one_by_scope_and_name( name=key_ref, scope=scope, from_model_kwargs=from_model_kwargs ) return kvp_api
def _validate_encrypted_query_parameter(self, encrypted, scope, requester_user): is_admin = rbac_utils.user_is_admin(user_db=requester_user) if encrypted and not is_admin: msg = 'Pre-encrypted option requires administrator access' raise AccessDeniedError(message=msg, user_db=requester_user)
def get_all(self, requester_user, prefix=None, scope=FULL_SYSTEM_SCOPE, user=None, decrypt=False, sort=None, offset=0, limit=None, **raw_filters): """ List all keys. Handles requests: GET /keys/ """ if not scope: # Default to system scope scope = FULL_SYSTEM_SCOPE if user: # Providing a user implies a user scope scope = FULL_USER_SCOPE if not requester_user: requester_user = UserDB(cfg.CONF.system_user.user) scope = get_datastore_full_scope(scope) # "all" scope can only be used by the admins (on RBAC installations) self._validate_all_scope(scope=scope, requester_user=requester_user) # User needs to be either admin or requesting items for themselves self._validate_decrypt_query_parameter(decrypt=decrypt, scope=scope, requester_user=requester_user) user_query_param_filter = bool(user) current_user = requester_user.name user = user or requester_user.name # Validate that the authenticated user is admin if user query param is provided assert_user_is_admin_if_user_query_param_is_provided(user_db=requester_user, user=user, require_rbac=True) from_model_kwargs = {'mask_secrets': not decrypt} if scope and scope not in ALL_SCOPE: self._validate_scope(scope=scope) raw_filters['scope'] = scope # Set prefix which will be used for user-scoped items. # NOTE: It's very important raw_filters['prefix'] is set when requesting user scoped items # to avoid information leakage (aka user1 retrieves items for user2) is_admin = rbac_utils.user_is_admin(user_db=requester_user) if is_admin and user_query_param_filter: # Retrieve values scoped to the provided user user_scope_prefix = get_key_reference(name=prefix or '', scope=USER_SCOPE, user=user) else: # RBAC not enabled or user is not an admin, retrieve user scoped values for the # current user user_scope_prefix = get_key_reference(name=prefix or '', scope=USER_SCOPE, user=current_user) if scope == ALL_SCOPE: # Special case for ALL_SCOPE # 1. Retrieve system scoped values raw_filters['scope'] = FULL_SYSTEM_SCOPE raw_filters['prefix'] = prefix assert 'scope' in raw_filters kvp_apis_system = super(KeyValuePairController, self)._get_all( from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user) # 2. Retrieve user scoped items for current user or for all the users (depending if the # authenticated user is admin and if ?user is provided) raw_filters['scope'] = FULL_USER_SCOPE if cfg.CONF.rbac.enable and is_admin and not user_query_param_filter: # Admin user retrieving user-scoped items for all the users raw_filters['prefix'] = prefix or '' else: raw_filters['prefix'] = user_scope_prefix assert 'scope' in raw_filters assert 'prefix' in raw_filters kvp_apis_user = super(KeyValuePairController, self)._get_all( from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user) # Combine the result kvp_apis = [] kvp_apis.extend(kvp_apis_system.json or []) kvp_apis.extend(kvp_apis_user.json or []) elif scope in [USER_SCOPE, FULL_USER_SCOPE]: # Make sure we only returned values scoped to current user prefix = get_key_reference(name=prefix or '', scope=scope, user=user) raw_filters['prefix'] = user_scope_prefix assert 'scope' in raw_filters assert 'prefix' in raw_filters kvp_apis = super(KeyValuePairController, self)._get_all( from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user) elif scope in [SYSTEM_SCOPE, FULL_SYSTEM_SCOPE]: raw_filters['prefix'] = prefix assert 'scope' in raw_filters kvp_apis = super(KeyValuePairController, self)._get_all( from_model_kwargs=from_model_kwargs, sort=sort, offset=offset, limit=limit, raw_filters=raw_filters, requester_user=requester_user) else: raise ValueError('Invalid scope: %s' % (scope)) return kvp_apis
def test_feature_flag_returns_true_on_rbac_disabled(self): # When feature RBAC is disabled, all the functions should return True cfg.CONF.set_override(name='enable', override=False, group='rbac') result = utils.user_is_admin(user_db=self.mocks['user_db']) self.assertTrue(result)