def check_alarm_actions(alarm): max_actions = pecan.request.cfg.api.alarm_max_actions for state in state_kind: actions_name = state.replace(" ", "_") + "_actions" actions = getattr(alarm, actions_name) if not actions: continue action_set = set(actions) if len(actions) != len(action_set): LOG.info(_LI("duplicate actions are found: %s, " "remove duplicate ones"), actions) actions = list(action_set) setattr(alarm, actions_name, actions) if 0 < max_actions < len(actions): error = _("%(name)s count exceeds maximum value " "%(maximum)d") % { "name": actions_name, "maximum": max_actions, } raise base.ClientSideError(error) limited = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) for action in actions: try: url = netutils.urlsplit(action) except Exception: error = _("Unable to parse action %s") % action raise base.ClientSideError(error) if url.scheme not in ACTIONS_SCHEMA: error = _("Unsupported action %s") % action raise base.ClientSideError(error) if limited and url.scheme in ("log", "test"): error = _("You are not authorized to create " "action: %s") % action raise base.ClientSideError(error, status_code=401)
def history(self, q=None, sort=None, limit=None, marker=None): """Assembles the alarm history requested. :param q: Filter rules for the changes to be described. :param sort: A list of pairs of sort key and sort dir. :param limit: The maximum number of items to be return. :param marker: The pagination query marker. """ # Ensure alarm exists self._enforce_rbac('alarm_history') q = q or [] # allow history to be returned for deleted alarms, but scope changes # returned to those carried out on behalf of the auth'd tenant, to # avoid inappropriate cross-tenant visibility of alarm history auth_project = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) conn = pecan.request.storage kwargs = v2_utils.query_to_kwargs( q, conn.get_alarm_changes, ['on_behalf_of', 'alarm_id']) if sort or limit or marker: kwargs['pagination'] = v2_utils.get_pagination_options( sort, limit, marker, models.AlarmChange) return [AlarmChange.from_db_model(ac) for ac in conn.get_alarm_changes(self._id, auth_project, **kwargs)]
def history(self, q=None, sort=None, limit=None, marker=None): """Assembles the alarm history requested. :param q: Filter rules for the changes to be described. :param sort: A list of pairs of sort key and sort dir. :param limit: The maximum number of items to be return. :param marker: The pagination query marker. """ # Ensure alarm exists self._enforce_rbac('alarm_history') q = q or [] # allow history to be returned for deleted alarms, but scope changes # returned to those carried out on behalf of the auth'd tenant, to # avoid inappropriate cross-tenant visibility of alarm history auth_project = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) conn = pecan.request.storage kwargs = v2_utils.query_to_kwargs(q, conn.get_alarm_changes, ['on_behalf_of', 'alarm_id']) if sort or limit or marker: kwargs['pagination'] = v2_utils.get_pagination_options( sort, limit, marker, models.AlarmChange) return [ AlarmChange.from_db_model(ac) for ac in conn.get_alarm_changes(self._id, auth_project, **kwargs) ]
def _alarm(self): self.conn = pecan.request.alarm_storage_conn auth_project = rbac.get_limited_to_project(pecan.request.headers) alarms = list(self.conn.get_alarms(alarm_id=self._id, project=auth_project)) if not alarms: raise base.AlarmNotFound(alarm=self._id, auth_project=auth_project) return alarms[0]
def _alarm(self): self.conn = pecan.request.alarm_storage_conn auth_project = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) alarms = list( self.conn.get_alarms(alarm_id=self._id, project=auth_project)) if not alarms: raise base.AlarmNotFound(alarm=self._id, auth_project=auth_project) return alarms[0]
def _verify_query_segregation(query, auth_project=None): """Ensure non-admin queries are not constrained to another project.""" auth_project = (auth_project or rbac.get_limited_to_project( pecan.request.headers, pecan.request.enforcer)) if not auth_project: return for q in query: if q.field in ('project', 'project_id') and auth_project != q.value: raise base.ProjectNotAuthorized(q.value)
def _enforce_rbac(self, rbac_directive): # TODO(sileht): We should be able to relax this since we # pass the alarm object to the enforcer. auth_project = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) alarms = list(pecan.request.storage.get_alarms(alarm_id=self._id, project=auth_project)) if not alarms: raise base.AlarmNotFound(alarm=self._id, auth_project=auth_project) alarm = alarms[0] target = {"user_id": alarm.user_id, "project_id": alarm.project_id} rbac.enforce(rbac_directive, pecan.request.headers, pecan.request.enforcer, target) return alarm
def _verify_query_segregation(query, auth_project=None): """Ensure non-admin queries are not constrained to another project.""" auth_project = (auth_project or rbac.get_limited_to_project(pecan.request.headers)) if not auth_project: return for q in query: if q.field in ('project', 'project_id') and auth_project != q.value: raise base.ProjectNotAuthorized(q.value)
def _force_visibility(self, visibility_field): """Force visibility field. If the tenant is not admin insert an extra "and <visibility_field>=<tenant's project_id>" clause to the query. """ authorized_project = rbac.get_limited_to_project(pecan.request.headers) is_admin = authorized_project is None if not is_admin: self._restrict_to_project(authorized_project, visibility_field) self._check_cross_project_references(authorized_project, visibility_field)
def _force_visibility(self, visibility_field): """Force visibility field. If the tenant is not admin insert an extra "and <visibility_field>=<tenant's project_id>" clause to the query. """ authorized_project = rbac.get_limited_to_project( pecan.request.headers, pecan.request.enforcer) is_admin = authorized_project is None if not is_admin: self._restrict_to_project(authorized_project, visibility_field) self._check_cross_project_references(authorized_project, visibility_field)
def _enforce_rbac(self, rbac_directive): # TODO(sileht): We should be able to relax this since we # pass the alarm object to the enforcer. auth_project = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) alarms = list( pecan.request.storage.get_alarms(alarm_id=self._id, project=auth_project)) if not alarms: raise base.AlarmNotFound(alarm=self._id, auth_project=auth_project) alarm = alarms[0] target = {'user_id': alarm.user_id, 'project_id': alarm.project_id} rbac.enforce(rbac_directive, pecan.request.headers, pecan.request.enforcer, target) return alarm
def get_auth_project(on_behalf_of=None): # when an alarm is created by an admin on behalf of another tenant # we must ensure for: # - threshold alarm, that an implicit query constraint on project_id is # added so that admin-level visibility on statistics is not leaked # Hence, for null auth_project (indicating admin-ness) we check if # the creating tenant differs from the tenant on whose behalf the # alarm is being created auth_project = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) created_by = pecan.request.headers.get('X-Project-Id') is_admin = auth_project is None if is_admin and on_behalf_of != created_by: auth_project = on_behalf_of return auth_project
def get_auth_project(on_behalf_of=None): # when an alarm is created by an admin on behalf of another tenant # we must ensure for: # - threshold alarm, that an implicit query constraint on project_id is # added so that admin-level visibility on statistics is not leaked # - combination alarm, that alarm ids verification is scoped to # alarms owned by the alarm project. # hence for null auth_project (indicating admin-ness) we check if # the creating tenant differs from the tenant on whose behalf the # alarm is being created auth_project = rbac.get_limited_to_project(pecan.request.headers) created_by = pecan.request.headers.get('X-Project-Id') is_admin = auth_project is None if is_admin and on_behalf_of != created_by: auth_project = on_behalf_of return auth_project
def history(self, q=None): """Assembles the alarm history requested. :param q: Filter rules for the changes to be described. """ rbac.enforce('alarm_history', pecan.request) q = q or [] # allow history to be returned for deleted alarms, but scope changes # returned to those carried out on behalf of the auth'd tenant, to # avoid inappropriate cross-tenant visibility of alarm history auth_project = rbac.get_limited_to_project(pecan.request.headers) conn = pecan.request.alarm_storage_conn kwargs = v2_utils.query_to_kwargs( q, conn.get_alarm_changes, ['on_behalf_of', 'alarm_id']) return [AlarmChange.from_db_model(ac) for ac in conn.get_alarm_changes(self._id, auth_project, **kwargs)]
def check_alarm_actions(alarm): max_actions = pecan.request.cfg.api.alarm_max_actions for state in state_kind: actions_name = state.replace(" ", "_") + '_actions' actions = getattr(alarm, actions_name) if not actions: continue action_set = set(actions) if len(actions) != len(action_set): LOG.info( 'duplicate actions are found: %s, ' 'remove duplicate ones', actions) actions = list(action_set) setattr(alarm, actions_name, actions) if 0 < max_actions < len(actions): error = _('%(name)s count exceeds maximum value ' '%(maximum)d') % { "name": actions_name, "maximum": max_actions } raise base.ClientSideError(error) limited = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) for action in actions: try: url = netutils.urlsplit(action) except Exception: error = _("Unable to parse action %s") % action raise base.ClientSideError(error) if url.scheme not in ACTIONS_SCHEMA: error = _("Unsupported action %s") % action raise base.ClientSideError(error) if limited and url.scheme in ('log', 'test'): error = _('You are not authorized to create ' 'action: %s') % action raise base.ClientSideError(error, status_code=401)
def history(self, q=None): """Assembles the alarm history requested. :param q: Filter rules for the changes to be described. """ rbac.enforce('alarm_history', pecan.request.headers, pecan.request.enforcer) q = q or [] # allow history to be returned for deleted alarms, but scope changes # returned to those carried out on behalf of the auth'd tenant, to # avoid inappropriate cross-tenant visibility of alarm history auth_project = rbac.get_limited_to_project(pecan.request.headers, pecan.request.enforcer) conn = pecan.request.alarm_storage_conn kwargs = v2_utils.query_to_kwargs(q, conn.get_alarm_changes, ['on_behalf_of', 'alarm_id']) return [ AlarmChange.from_db_model(ac) for ac in conn.get_alarm_changes(self._id, auth_project, **kwargs) ]
def check_alarm_actions(alarm): actions_schema = aodh_alarm.NOTIFIER_SCHEMAS max_actions = cfg.CONF.alarm.alarm_max_actions for state in state_kind: actions_name = state.replace(" ", "_") + '_actions' actions = getattr(alarm, actions_name) if not actions: continue action_set = set(actions) if len(actions) != len(action_set): LOG.info(_('duplicate actions are found: %s, ' 'remove duplicate ones') % actions) actions = list(action_set) setattr(alarm, actions_name, actions) if 0 < max_actions < len(actions): error = _('%(name)s count exceeds maximum value ' '%(maximum)d') % {"name": actions_name, "maximum": max_actions} raise base.ClientSideError(error) limited = rbac.get_limited_to_project(pecan.request.headers) for action in actions: try: url = netutils.urlsplit(action) except Exception: error = _("Unable to parse action %s") % action raise base.ClientSideError(error) if url.scheme not in actions_schema: error = _("Unsupported action %s") % action raise base.ClientSideError(error) if limited and url.scheme in ('log', 'test'): error = _('You are not authorized to create ' 'action: %s') % action raise base.ClientSideError(error, status_code=401)