"Fossies" - the Fresh Open Source Software Archive

Member "keystone-19.0.0/keystone/assignment/backends/sql.py" (14 Apr 2021, 17315 Bytes) of package /linux/misc/openstack/keystone-19.0.0.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "sql.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 18.0.0_vs_19.0.0.

    1 # Copyright 2012-13 OpenStack Foundation
    2 #
    3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
    4 # not use this file except in compliance with the License. You may obtain
    5 # a copy of the License at
    6 #
    7 #      http://www.apache.org/licenses/LICENSE-2.0
    8 #
    9 # Unless required by applicable law or agreed to in writing, software
   10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
   11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
   12 # License for the specific language governing permissions and limitations
   13 # under the License.
   14 
   15 from keystone.assignment.backends import base
   16 from keystone.common import sql
   17 from keystone import exception
   18 from keystone.i18n import _
   19 
   20 
   21 class AssignmentType(object):
   22     USER_PROJECT = 'UserProject'
   23     GROUP_PROJECT = 'GroupProject'
   24     USER_DOMAIN = 'UserDomain'
   25     GROUP_DOMAIN = 'GroupDomain'
   26 
   27     @classmethod
   28     def calculate_type(cls, user_id, group_id, project_id, domain_id):
   29         if user_id:
   30             if project_id:
   31                 return cls.USER_PROJECT
   32             if domain_id:
   33                 return cls.USER_DOMAIN
   34         if group_id:
   35             if project_id:
   36                 return cls.GROUP_PROJECT
   37             if domain_id:
   38                 return cls.GROUP_DOMAIN
   39         # Invalid parameters combination
   40         raise exception.AssignmentTypeCalculationError(**locals())
   41 
   42 
   43 class Assignment(base.AssignmentDriverBase):
   44 
   45     @classmethod
   46     def default_role_driver(cls):
   47         return 'sql'
   48 
   49     def create_grant(self, role_id, user_id=None, group_id=None,
   50                      domain_id=None, project_id=None,
   51                      inherited_to_projects=False):
   52 
   53         assignment_type = AssignmentType.calculate_type(
   54             user_id, group_id, project_id, domain_id)
   55         try:
   56             with sql.session_for_write() as session:
   57                 session.add(RoleAssignment(
   58                     type=assignment_type,
   59                     actor_id=user_id or group_id,
   60                     target_id=project_id or domain_id,
   61                     role_id=role_id,
   62                     inherited=inherited_to_projects))
   63         except sql.DBDuplicateEntry:  # nosec : The v3 grant APIs are silent if
   64             # the assignment already exists
   65             pass
   66 
   67     def list_grant_role_ids(self, user_id=None, group_id=None,
   68                             domain_id=None, project_id=None,
   69                             inherited_to_projects=False):
   70         with sql.session_for_read() as session:
   71             q = session.query(RoleAssignment.role_id)
   72             q = q.filter(RoleAssignment.actor_id == (user_id or group_id))
   73             q = q.filter(RoleAssignment.target_id == (project_id or domain_id))
   74             q = q.filter(RoleAssignment.inherited == inherited_to_projects)
   75             return [x.role_id for x in q.all()]
   76 
   77     def _build_grant_filter(self, session, role_id, user_id, group_id,
   78                             domain_id, project_id, inherited_to_projects):
   79         q = session.query(RoleAssignment)
   80         q = q.filter_by(actor_id=user_id or group_id)
   81         if domain_id:
   82             q = q.filter_by(target_id=domain_id).filter(
   83                 (RoleAssignment.type == AssignmentType.USER_DOMAIN) |
   84                 (RoleAssignment.type == AssignmentType.GROUP_DOMAIN))
   85         else:
   86             q = q.filter_by(target_id=project_id).filter(
   87                 (RoleAssignment.type == AssignmentType.USER_PROJECT) |
   88                 (RoleAssignment.type == AssignmentType.GROUP_PROJECT))
   89         q = q.filter_by(role_id=role_id)
   90         q = q.filter_by(inherited=inherited_to_projects)
   91         return q
   92 
   93     def check_grant_role_id(self, role_id, user_id=None, group_id=None,
   94                             domain_id=None, project_id=None,
   95                             inherited_to_projects=False):
   96         with sql.session_for_read() as session:
   97             try:
   98                 q = self._build_grant_filter(
   99                     session, role_id, user_id, group_id, domain_id, project_id,
  100                     inherited_to_projects)
  101                 q.one()
  102             except sql.NotFound:
  103                 actor_id = user_id or group_id
  104                 target_id = domain_id or project_id
  105                 raise exception.RoleAssignmentNotFound(role_id=role_id,
  106                                                        actor_id=actor_id,
  107                                                        target_id=target_id)
  108 
  109     def delete_grant(self, role_id, user_id=None, group_id=None,
  110                      domain_id=None, project_id=None,
  111                      inherited_to_projects=False):
  112         with sql.session_for_write() as session:
  113             q = self._build_grant_filter(
  114                 session, role_id, user_id, group_id, domain_id, project_id,
  115                 inherited_to_projects)
  116             if not q.delete(False):
  117                 actor_id = user_id or group_id
  118                 target_id = domain_id or project_id
  119                 raise exception.RoleAssignmentNotFound(role_id=role_id,
  120                                                        actor_id=actor_id,
  121                                                        target_id=target_id)
  122 
  123     def add_role_to_user_and_project(self, user_id, project_id, role_id):
  124         try:
  125             with sql.session_for_write() as session:
  126                 session.add(RoleAssignment(
  127                     type=AssignmentType.USER_PROJECT,
  128                     actor_id=user_id, target_id=project_id,
  129                     role_id=role_id, inherited=False))
  130         except sql.DBDuplicateEntry:
  131             msg = ('User %s already has role %s in tenant %s'
  132                    % (user_id, role_id, project_id))
  133             raise exception.Conflict(type='role grant', details=msg)
  134 
  135     def remove_role_from_user_and_project(self, user_id, project_id, role_id):
  136         with sql.session_for_write() as session:
  137             q = session.query(RoleAssignment)
  138             q = q.filter_by(actor_id=user_id)
  139             q = q.filter_by(target_id=project_id)
  140             q = q.filter_by(role_id=role_id)
  141             if q.delete() == 0:
  142                 raise exception.RoleNotFound(message=_(
  143                     'Cannot remove role that has not been granted, %s') %
  144                     role_id)
  145 
  146     def _get_user_assignment_types(self):
  147         return [AssignmentType.USER_PROJECT, AssignmentType.USER_DOMAIN]
  148 
  149     def _get_group_assignment_types(self):
  150         return [AssignmentType.GROUP_PROJECT, AssignmentType.GROUP_DOMAIN]
  151 
  152     def _get_project_assignment_types(self):
  153         return [AssignmentType.USER_PROJECT, AssignmentType.GROUP_PROJECT]
  154 
  155     def _get_domain_assignment_types(self):
  156         return [AssignmentType.USER_DOMAIN, AssignmentType.GROUP_DOMAIN]
  157 
  158     def _get_assignment_types(self, user, group, project, domain):
  159         """Return a list of role assignment types based on provided entities.
  160 
  161         If one of user or group (the "actor") as well as one of project or
  162         domain (the "target") are provided, the list will contain the role
  163         assignment type for that specific pair of actor and target.
  164 
  165         If only an actor or target is provided, the list will contain the
  166         role assignment types that satisfy the specified entity.
  167 
  168         For example, if user and project are provided, the return will be:
  169 
  170             [AssignmentType.USER_PROJECT]
  171 
  172         However, if only user was provided, the return would be:
  173 
  174             [AssignmentType.USER_PROJECT, AssignmentType.USER_DOMAIN]
  175 
  176         It is not expected that user and group (or project and domain) are
  177         specified - but if they are, the most fine-grained value will be
  178         chosen (i.e. user over group, project over domain).
  179 
  180         """
  181         actor_types = []
  182         if user:
  183             actor_types = self._get_user_assignment_types()
  184         elif group:
  185             actor_types = self._get_group_assignment_types()
  186 
  187         target_types = []
  188         if project:
  189             target_types = self._get_project_assignment_types()
  190         elif domain:
  191             target_types = self._get_domain_assignment_types()
  192 
  193         if actor_types and target_types:
  194             return list(set(actor_types).intersection(target_types))
  195 
  196         return actor_types or target_types
  197 
  198     def list_role_assignments(self, role_id=None,
  199                               user_id=None, group_ids=None,
  200                               domain_id=None, project_ids=None,
  201                               inherited_to_projects=None):
  202 
  203         def denormalize_role(ref):
  204             assignment = {}
  205             if ref.type == AssignmentType.USER_PROJECT:
  206                 assignment['user_id'] = ref.actor_id
  207                 assignment['project_id'] = ref.target_id
  208             elif ref.type == AssignmentType.USER_DOMAIN:
  209                 assignment['user_id'] = ref.actor_id
  210                 assignment['domain_id'] = ref.target_id
  211             elif ref.type == AssignmentType.GROUP_PROJECT:
  212                 assignment['group_id'] = ref.actor_id
  213                 assignment['project_id'] = ref.target_id
  214             elif ref.type == AssignmentType.GROUP_DOMAIN:
  215                 assignment['group_id'] = ref.actor_id
  216                 assignment['domain_id'] = ref.target_id
  217             else:
  218                 raise exception.Error(message=_(
  219                     'Unexpected assignment type encountered, %s') %
  220                     ref.type)
  221             assignment['role_id'] = ref.role_id
  222             if ref.inherited:
  223                 assignment['inherited_to_projects'] = 'projects'
  224             return assignment
  225 
  226         with sql.session_for_read() as session:
  227             assignment_types = self._get_assignment_types(
  228                 user_id, group_ids, project_ids, domain_id)
  229 
  230             targets = None
  231             if project_ids:
  232                 targets = project_ids
  233             elif domain_id:
  234                 targets = [domain_id]
  235 
  236             actors = None
  237             if group_ids:
  238                 actors = group_ids
  239             elif user_id:
  240                 actors = [user_id]
  241 
  242             query = session.query(RoleAssignment)
  243 
  244             if role_id:
  245                 query = query.filter_by(role_id=role_id)
  246             if actors:
  247                 query = query.filter(RoleAssignment.actor_id.in_(actors))
  248             if targets:
  249                 query = query.filter(RoleAssignment.target_id.in_(targets))
  250             if assignment_types:
  251                 query = query.filter(RoleAssignment.type.in_(assignment_types))
  252             if inherited_to_projects is not None:
  253                 query = query.filter_by(inherited=inherited_to_projects)
  254 
  255             return [denormalize_role(ref) for ref in query.all()]
  256 
  257     def delete_project_assignments(self, project_id):
  258         with sql.session_for_write() as session:
  259             q = session.query(RoleAssignment)
  260             q = q.filter_by(target_id=project_id).filter(
  261                 RoleAssignment.type.in_((AssignmentType.USER_PROJECT,
  262                                          AssignmentType.GROUP_PROJECT))
  263             )
  264             q.delete(False)
  265 
  266     def delete_role_assignments(self, role_id):
  267         with sql.session_for_write() as session:
  268             q = session.query(RoleAssignment)
  269             q = q.filter_by(role_id=role_id)
  270             q.delete(False)
  271 
  272         with sql.session_for_write() as session:
  273             q = session.query(SystemRoleAssignment)
  274             q = q.filter_by(role_id=role_id)
  275             q.delete(False)
  276 
  277     def delete_domain_assignments(self, domain_id):
  278         with sql.session_for_write() as session:
  279             q = session.query(RoleAssignment)
  280             q = q.filter(RoleAssignment.target_id == domain_id).filter(
  281                 (RoleAssignment.type == AssignmentType.USER_DOMAIN) |
  282                 (RoleAssignment.type == AssignmentType.GROUP_DOMAIN))
  283             q.delete(False)
  284 
  285     def delete_user_assignments(self, user_id):
  286         with sql.session_for_write() as session:
  287             q = session.query(RoleAssignment)
  288             q = q.filter_by(actor_id=user_id).filter(
  289                 RoleAssignment.type.in_((AssignmentType.USER_PROJECT,
  290                                          AssignmentType.USER_DOMAIN))
  291             )
  292             q.delete(False)
  293 
  294     def delete_group_assignments(self, group_id):
  295         with sql.session_for_write() as session:
  296             q = session.query(RoleAssignment)
  297             q = q.filter_by(actor_id=group_id).filter(
  298                 RoleAssignment.type.in_((AssignmentType.GROUP_PROJECT,
  299                                          AssignmentType.GROUP_DOMAIN))
  300             )
  301             q.delete(False)
  302 
  303     def create_system_grant(self, role_id, actor_id, target_id,
  304                             assignment_type, inherited):
  305         try:
  306             with sql.session_for_write() as session:
  307                 session.add(
  308                     SystemRoleAssignment(
  309                         type=assignment_type,
  310                         actor_id=actor_id,
  311                         target_id=target_id,
  312                         role_id=role_id,
  313                         inherited=inherited
  314                     )
  315                 )
  316         except sql.DBDuplicateEntry:  # nosec : The v3 grant APIs are silent if
  317             # the assignment already exists
  318             pass
  319 
  320     def list_system_grants(self, actor_id, target_id, assignment_type):
  321         with sql.session_for_read() as session:
  322             query = session.query(SystemRoleAssignment)
  323             if actor_id:
  324                 query = query.filter_by(actor_id=actor_id)
  325             if target_id:
  326                 query = query.filter_by(target_id=target_id)
  327             if assignment_type:
  328                 query = query.filter_by(type=assignment_type)
  329             results = query.all()
  330 
  331         return [role.to_dict() for role in results]
  332 
  333     def list_system_grants_by_role(self, role_id):
  334         with sql.session_for_read() as session:
  335             query = session.query(SystemRoleAssignment)
  336             query = query.filter_by(role_id=role_id)
  337             return query.all()
  338 
  339     def check_system_grant(self, role_id, actor_id, target_id, inherited):
  340         with sql.session_for_read() as session:
  341             try:
  342                 q = session.query(SystemRoleAssignment)
  343                 q = q.filter_by(actor_id=actor_id)
  344                 q = q.filter_by(target_id=target_id)
  345                 q = q.filter_by(role_id=role_id)
  346                 q = q.filter_by(inherited=inherited)
  347                 q.one()
  348             except sql.NotFound:
  349                 raise exception.RoleAssignmentNotFound(
  350                     role_id=role_id, actor_id=actor_id, target_id=target_id
  351                 )
  352 
  353     def delete_system_grant(self, role_id, actor_id, target_id, inherited):
  354         with sql.session_for_write() as session:
  355             q = session.query(SystemRoleAssignment)
  356             q = q.filter_by(actor_id=actor_id)
  357             q = q.filter_by(target_id=target_id)
  358             q = q.filter_by(role_id=role_id)
  359             q = q.filter_by(inherited=inherited)
  360             if not q.delete(False):
  361                 raise exception.RoleAssignmentNotFound(
  362                     role_id=role_id, actor_id=actor_id, target_id=target_id
  363                 )
  364 
  365 
  366 class RoleAssignment(sql.ModelBase, sql.ModelDictMixin):
  367     __tablename__ = 'assignment'
  368     attributes = ['type', 'actor_id', 'target_id', 'role_id', 'inherited']
  369     # NOTE(henry-nash): Postgres requires a name to be defined for an Enum
  370     type = sql.Column(
  371         sql.Enum(AssignmentType.USER_PROJECT, AssignmentType.GROUP_PROJECT,
  372                  AssignmentType.USER_DOMAIN, AssignmentType.GROUP_DOMAIN,
  373                  name='type'),
  374         nullable=False)
  375     actor_id = sql.Column(sql.String(64), nullable=False)
  376     target_id = sql.Column(sql.String(64), nullable=False)
  377     role_id = sql.Column(sql.String(64), nullable=False)
  378     inherited = sql.Column(sql.Boolean, default=False, nullable=False)
  379     __table_args__ = (
  380         sql.PrimaryKeyConstraint('type', 'actor_id', 'target_id', 'role_id',
  381                                  'inherited'),
  382         sql.Index('ix_actor_id', 'actor_id'),
  383     )
  384 
  385     def to_dict(self):
  386         """Override parent method with a simpler implementation.
  387 
  388         RoleAssignment doesn't have non-indexed 'extra' attributes, so the
  389         parent implementation is not applicable.
  390         """
  391         return dict(self.items())
  392 
  393 
  394 class SystemRoleAssignment(sql.ModelBase, sql.ModelDictMixin):
  395     __tablename__ = 'system_assignment'
  396     attributes = ['type', 'actor_id', 'target_id', 'role_id', 'inherited']
  397     type = sql.Column(sql.String(64), nullable=False)
  398     actor_id = sql.Column(sql.String(64), nullable=False)
  399     target_id = sql.Column(sql.String(64), nullable=False)
  400     role_id = sql.Column(sql.String(64), nullable=False)
  401     inherited = sql.Column(sql.Boolean, default=False, nullable=False)
  402     __table_args__ = (
  403         sql.PrimaryKeyConstraint('type', 'actor_id', 'target_id', 'role_id',
  404                                  'inherited'),
  405         sql.Index('ix_system_actor_id', 'actor_id'),
  406     )
  407 
  408     def to_dict(self):
  409         """Override parent method with a simpler implementation.
  410 
  411         RoleAssignment doesn't have non-indexed 'extra' attributes, so the
  412         parent implementation is not applicable.
  413         """
  414         return dict(self.items())