"Fossies" - the Fresh Open Source Software Archive

Member "keystone-16.0.2/keystone/assignment/backends/sql.py" (7 Jun 2021, 16941 Bytes) of package /linux/misc/openstack/keystone-16.0.2.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: 16.0.1_vs_16.0.2.

    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         q = q.filter_by(target_id=project_id or domain_id)
   82         q = q.filter_by(role_id=role_id)
   83         q = q.filter_by(inherited=inherited_to_projects)
   84         return q
   85 
   86     def check_grant_role_id(self, role_id, user_id=None, group_id=None,
   87                             domain_id=None, project_id=None,
   88                             inherited_to_projects=False):
   89         with sql.session_for_read() as session:
   90             try:
   91                 q = self._build_grant_filter(
   92                     session, role_id, user_id, group_id, domain_id, project_id,
   93                     inherited_to_projects)
   94                 q.one()
   95             except sql.NotFound:
   96                 actor_id = user_id or group_id
   97                 target_id = domain_id or project_id
   98                 raise exception.RoleAssignmentNotFound(role_id=role_id,
   99                                                        actor_id=actor_id,
  100                                                        target_id=target_id)
  101 
  102     def delete_grant(self, role_id, user_id=None, group_id=None,
  103                      domain_id=None, project_id=None,
  104                      inherited_to_projects=False):
  105         with sql.session_for_write() as session:
  106             q = self._build_grant_filter(
  107                 session, role_id, user_id, group_id, domain_id, project_id,
  108                 inherited_to_projects)
  109             if not q.delete(False):
  110                 actor_id = user_id or group_id
  111                 target_id = domain_id or project_id
  112                 raise exception.RoleAssignmentNotFound(role_id=role_id,
  113                                                        actor_id=actor_id,
  114                                                        target_id=target_id)
  115 
  116     def add_role_to_user_and_project(self, user_id, project_id, role_id):
  117         try:
  118             with sql.session_for_write() as session:
  119                 session.add(RoleAssignment(
  120                     type=AssignmentType.USER_PROJECT,
  121                     actor_id=user_id, target_id=project_id,
  122                     role_id=role_id, inherited=False))
  123         except sql.DBDuplicateEntry:
  124             msg = ('User %s already has role %s in tenant %s'
  125                    % (user_id, role_id, project_id))
  126             raise exception.Conflict(type='role grant', details=msg)
  127 
  128     def remove_role_from_user_and_project(self, user_id, project_id, role_id):
  129         with sql.session_for_write() as session:
  130             q = session.query(RoleAssignment)
  131             q = q.filter_by(actor_id=user_id)
  132             q = q.filter_by(target_id=project_id)
  133             q = q.filter_by(role_id=role_id)
  134             if q.delete() == 0:
  135                 raise exception.RoleNotFound(message=_(
  136                     'Cannot remove role that has not been granted, %s') %
  137                     role_id)
  138 
  139     def _get_user_assignment_types(self):
  140         return [AssignmentType.USER_PROJECT, AssignmentType.USER_DOMAIN]
  141 
  142     def _get_group_assignment_types(self):
  143         return [AssignmentType.GROUP_PROJECT, AssignmentType.GROUP_DOMAIN]
  144 
  145     def _get_project_assignment_types(self):
  146         return [AssignmentType.USER_PROJECT, AssignmentType.GROUP_PROJECT]
  147 
  148     def _get_domain_assignment_types(self):
  149         return [AssignmentType.USER_DOMAIN, AssignmentType.GROUP_DOMAIN]
  150 
  151     def _get_assignment_types(self, user, group, project, domain):
  152         """Return a list of role assignment types based on provided entities.
  153 
  154         If one of user or group (the "actor") as well as one of project or
  155         domain (the "target") are provided, the list will contain the role
  156         assignment type for that specific pair of actor and target.
  157 
  158         If only an actor or target is provided, the list will contain the
  159         role assignment types that satisfy the specified entity.
  160 
  161         For example, if user and project are provided, the return will be:
  162 
  163             [AssignmentType.USER_PROJECT]
  164 
  165         However, if only user was provided, the return would be:
  166 
  167             [AssignmentType.USER_PROJECT, AssignmentType.USER_DOMAIN]
  168 
  169         It is not expected that user and group (or project and domain) are
  170         specified - but if they are, the most fine-grained value will be
  171         chosen (i.e. user over group, project over domain).
  172 
  173         """
  174         actor_types = []
  175         if user:
  176             actor_types = self._get_user_assignment_types()
  177         elif group:
  178             actor_types = self._get_group_assignment_types()
  179 
  180         target_types = []
  181         if project:
  182             target_types = self._get_project_assignment_types()
  183         elif domain:
  184             target_types = self._get_domain_assignment_types()
  185 
  186         if actor_types and target_types:
  187             return list(set(actor_types).intersection(target_types))
  188 
  189         return actor_types or target_types
  190 
  191     def list_role_assignments(self, role_id=None,
  192                               user_id=None, group_ids=None,
  193                               domain_id=None, project_ids=None,
  194                               inherited_to_projects=None):
  195 
  196         def denormalize_role(ref):
  197             assignment = {}
  198             if ref.type == AssignmentType.USER_PROJECT:
  199                 assignment['user_id'] = ref.actor_id
  200                 assignment['project_id'] = ref.target_id
  201             elif ref.type == AssignmentType.USER_DOMAIN:
  202                 assignment['user_id'] = ref.actor_id
  203                 assignment['domain_id'] = ref.target_id
  204             elif ref.type == AssignmentType.GROUP_PROJECT:
  205                 assignment['group_id'] = ref.actor_id
  206                 assignment['project_id'] = ref.target_id
  207             elif ref.type == AssignmentType.GROUP_DOMAIN:
  208                 assignment['group_id'] = ref.actor_id
  209                 assignment['domain_id'] = ref.target_id
  210             else:
  211                 raise exception.Error(message=_(
  212                     'Unexpected assignment type encountered, %s') %
  213                     ref.type)
  214             assignment['role_id'] = ref.role_id
  215             if ref.inherited:
  216                 assignment['inherited_to_projects'] = 'projects'
  217             return assignment
  218 
  219         with sql.session_for_read() as session:
  220             assignment_types = self._get_assignment_types(
  221                 user_id, group_ids, project_ids, domain_id)
  222 
  223             targets = None
  224             if project_ids:
  225                 targets = project_ids
  226             elif domain_id:
  227                 targets = [domain_id]
  228 
  229             actors = None
  230             if group_ids:
  231                 actors = group_ids
  232             elif user_id:
  233                 actors = [user_id]
  234 
  235             query = session.query(RoleAssignment)
  236 
  237             if role_id:
  238                 query = query.filter_by(role_id=role_id)
  239             if actors:
  240                 query = query.filter(RoleAssignment.actor_id.in_(actors))
  241             if targets:
  242                 query = query.filter(RoleAssignment.target_id.in_(targets))
  243             if assignment_types:
  244                 query = query.filter(RoleAssignment.type.in_(assignment_types))
  245             if inherited_to_projects is not None:
  246                 query = query.filter_by(inherited=inherited_to_projects)
  247 
  248             return [denormalize_role(ref) for ref in query.all()]
  249 
  250     def delete_project_assignments(self, project_id):
  251         with sql.session_for_write() as session:
  252             q = session.query(RoleAssignment)
  253             q = q.filter_by(target_id=project_id).filter(
  254                 RoleAssignment.type.in_((AssignmentType.USER_PROJECT,
  255                                          AssignmentType.GROUP_PROJECT))
  256             )
  257             q.delete(False)
  258 
  259     def delete_role_assignments(self, role_id):
  260         with sql.session_for_write() as session:
  261             q = session.query(RoleAssignment)
  262             q = q.filter_by(role_id=role_id)
  263             q.delete(False)
  264 
  265         with sql.session_for_write() as session:
  266             q = session.query(SystemRoleAssignment)
  267             q = q.filter_by(role_id=role_id)
  268             q.delete(False)
  269 
  270     def delete_domain_assignments(self, domain_id):
  271         with sql.session_for_write() as session:
  272             q = session.query(RoleAssignment)
  273             q = q.filter(RoleAssignment.target_id == domain_id).filter(
  274                 (RoleAssignment.type == AssignmentType.USER_DOMAIN) |
  275                 (RoleAssignment.type == AssignmentType.GROUP_DOMAIN))
  276             q.delete(False)
  277 
  278     def delete_user_assignments(self, user_id):
  279         with sql.session_for_write() as session:
  280             q = session.query(RoleAssignment)
  281             q = q.filter_by(actor_id=user_id).filter(
  282                 RoleAssignment.type.in_((AssignmentType.USER_PROJECT,
  283                                          AssignmentType.USER_DOMAIN))
  284             )
  285             q.delete(False)
  286 
  287     def delete_group_assignments(self, group_id):
  288         with sql.session_for_write() as session:
  289             q = session.query(RoleAssignment)
  290             q = q.filter_by(actor_id=group_id).filter(
  291                 RoleAssignment.type.in_((AssignmentType.GROUP_PROJECT,
  292                                          AssignmentType.GROUP_DOMAIN))
  293             )
  294             q.delete(False)
  295 
  296     def create_system_grant(self, role_id, actor_id, target_id,
  297                             assignment_type, inherited):
  298         try:
  299             with sql.session_for_write() as session:
  300                 session.add(
  301                     SystemRoleAssignment(
  302                         type=assignment_type,
  303                         actor_id=actor_id,
  304                         target_id=target_id,
  305                         role_id=role_id,
  306                         inherited=inherited
  307                     )
  308                 )
  309         except sql.DBDuplicateEntry:  # nosec : The v3 grant APIs are silent if
  310             # the assignment already exists
  311             pass
  312 
  313     def list_system_grants(self, actor_id, target_id, assignment_type):
  314         with sql.session_for_read() as session:
  315             query = session.query(SystemRoleAssignment)
  316             if actor_id:
  317                 query = query.filter_by(actor_id=actor_id)
  318             if target_id:
  319                 query = query.filter_by(target_id=target_id)
  320             if assignment_type:
  321                 query = query.filter_by(type=assignment_type)
  322             results = query.all()
  323 
  324         return [role.to_dict() for role in results]
  325 
  326     def list_system_grants_by_role(self, role_id):
  327         with sql.session_for_read() as session:
  328             query = session.query(SystemRoleAssignment)
  329             query = query.filter_by(role_id=role_id)
  330             return query.all()
  331 
  332     def check_system_grant(self, role_id, actor_id, target_id, inherited):
  333         with sql.session_for_read() as session:
  334             try:
  335                 q = session.query(SystemRoleAssignment)
  336                 q = q.filter_by(actor_id=actor_id)
  337                 q = q.filter_by(target_id=target_id)
  338                 q = q.filter_by(role_id=role_id)
  339                 q = q.filter_by(inherited=inherited)
  340                 q.one()
  341             except sql.NotFound:
  342                 raise exception.RoleAssignmentNotFound(
  343                     role_id=role_id, actor_id=actor_id, target_id=target_id
  344                 )
  345 
  346     def delete_system_grant(self, role_id, actor_id, target_id, inherited):
  347         with sql.session_for_write() as session:
  348             q = session.query(SystemRoleAssignment)
  349             q = q.filter_by(actor_id=actor_id)
  350             q = q.filter_by(target_id=target_id)
  351             q = q.filter_by(role_id=role_id)
  352             q = q.filter_by(inherited=inherited)
  353             if not q.delete(False):
  354                 raise exception.RoleAssignmentNotFound(
  355                     role_id=role_id, actor_id=actor_id, target_id=target_id
  356                 )
  357 
  358 
  359 class RoleAssignment(sql.ModelBase, sql.ModelDictMixin):
  360     __tablename__ = 'assignment'
  361     attributes = ['type', 'actor_id', 'target_id', 'role_id', 'inherited']
  362     # NOTE(henry-nash): Postgres requires a name to be defined for an Enum
  363     type = sql.Column(
  364         sql.Enum(AssignmentType.USER_PROJECT, AssignmentType.GROUP_PROJECT,
  365                  AssignmentType.USER_DOMAIN, AssignmentType.GROUP_DOMAIN,
  366                  name='type'),
  367         nullable=False)
  368     actor_id = sql.Column(sql.String(64), nullable=False)
  369     target_id = sql.Column(sql.String(64), nullable=False)
  370     role_id = sql.Column(sql.String(64), nullable=False)
  371     inherited = sql.Column(sql.Boolean, default=False, nullable=False)
  372     __table_args__ = (
  373         sql.PrimaryKeyConstraint('type', 'actor_id', 'target_id', 'role_id',
  374                                  'inherited'),
  375         sql.Index('ix_actor_id', 'actor_id'),
  376     )
  377 
  378     def to_dict(self):
  379         """Override parent method with a simpler implementation.
  380 
  381         RoleAssignment doesn't have non-indexed 'extra' attributes, so the
  382         parent implementation is not applicable.
  383         """
  384         return dict(self.items())
  385 
  386 
  387 class SystemRoleAssignment(sql.ModelBase, sql.ModelDictMixin):
  388     __tablename__ = 'system_assignment'
  389     attributes = ['type', 'actor_id', 'target_id', 'role_id', 'inherited']
  390     type = sql.Column(sql.String(64), nullable=False)
  391     actor_id = sql.Column(sql.String(64), nullable=False)
  392     target_id = sql.Column(sql.String(64), nullable=False)
  393     role_id = sql.Column(sql.String(64), nullable=False)
  394     inherited = sql.Column(sql.Boolean, default=False, nullable=False)
  395     __table_args__ = (
  396         sql.PrimaryKeyConstraint('type', 'actor_id', 'target_id', 'role_id',
  397                                  'inherited'),
  398         sql.Index('ix_system_actor_id', 'actor_id'),
  399     )
  400 
  401     def to_dict(self):
  402         """Override parent method with a simpler implementation.
  403 
  404         RoleAssignment doesn't have non-indexed 'extra' attributes, so the
  405         parent implementation is not applicable.
  406         """
  407         return dict(self.items())