Exemple #1
0
 def test_remaps_permissions(self):
   out = realms.merge(
       perms('luci.dev.p1', 'luci.dev.p2'),
       {
           'proj1': realms_pb2.Realms(
               permissions=perms('luci.dev.p2', 'luci.dev.z', 'luci.dev.p1'),
               realms=[
                   {
                       'name': 'proj1:@root',
                       'bindings': [
                           {
                               'permissions': [0, 1, 2],  # p2, z, p1
                               'principals': ['group:gr1'],
                           },
                           {
                               'permissions': [1],  # z, will be dropped
                               'principals': ['group:gr2'],
                           },
                           {
                               'permissions': [2],  # p1
                               'principals': ['group:gr3'],
                           },
                       ],
                   },
               ],
           ),
       },
   )
   self.assertEqual(out, realms_pb2.Realms(
       api_version=realms.API_VERSION,
       permissions=perms('luci.dev.p1', 'luci.dev.p2'),
       realms=[
           {
               'name': 'proj1:@root',
               'bindings': [
                   {
                       'permissions': [0],  # p1
                       'principals': ['group:gr3'],
                   },
                   {
                       'permissions': [0, 1],  # p1, p2
                       'principals': ['group:gr1'],
                   },
               ],
           },
       ],
   ))
Exemple #2
0
 def test_merge_multiple(self):
   out = realms.merge(
       perms('luci.dev.p1', 'luci.dev.p2', 'luci.dev.p3'),
       {
           'proj1': realms_pb2.Realms(
               permissions=perms('luci.dev.p1', 'luci.dev.p2'),
               realms=[
                   {
                       'name': 'proj1:@root',
                       'bindings': [
                           {
                               'permissions': [0, 1],  # p1, p2
                               'principals': ['group:gr1'],
                           },
                       ],
                       'data': {
                           'enforce_in_service': ['a'],
                       },
                   },
               ],
           ),
           'proj2': realms_pb2.Realms(
               permissions=perms('luci.dev.p2', 'luci.dev.p3'),
               realms=[
                   {
                       'name': 'proj2:@root',
                       'bindings': [
                           {
                               'permissions': [0, 1],  # p2, p3
                               'principals': ['group:gr2'],
                           },
                       ],
                   },
               ],
           ),
       },
   )
   self.assertEqual(out, realms_pb2.Realms(
       api_version=realms.API_VERSION,
       permissions=perms('luci.dev.p1', 'luci.dev.p2', 'luci.dev.p3'),
       realms=[
           {
               'name': 'proj1:@root',
               'bindings': [
                   {
                       'permissions': [0, 1],  # p1, p2
                       'principals': ['group:gr1'],
                   },
               ],
               'data': {
                   'enforce_in_service': ['a'],
               },
           },
           {
               'name': 'proj2:@root',
               'bindings': [
                   {
                       'permissions': [1, 2],  # p2, p3
                       'principals': ['group:gr2'],
                   },
               ],
           },
       ],
   ))
Exemple #3
0
 def test_empty(self):
   self.assertEqual(
       realms.merge([], {}),
       realms_pb2.Realms(api_version=realms.API_VERSION))
Exemple #4
0
def expand_realms(db, project_id, realms_cfg):
    """Expands realms_config_pb2.RealmsCfg into a flat realms_pb2.Realms.

  The returned realms_pb2.Realms contains realms and permissions of a single
  project only. Permissions not mentioned in the project's realms are omitted.
  All realms_pb2.Permission messages have names only (no metadata). api_version
  field is omitted.

  All such realms_pb2.Realms messages across all projects (plus a list of all
  defined permissions with all their metadata) are later merged together into
  a final universal realms_pb2.Realms by realms.merge(...) in
  components/auth/replication.py.

  Args:
    db: a permissions.DB instance with current permissions and roles.
    project_id: ID of a LUCI project to use as a prefix in realm names.
    realms_cfg: an instance of realms_config_pb2.RealmsCfg to expand.

  Returns:
    realms_pb2.Realms with expanded realms (with caveats mentioned above).

  Raises:
    ValueError if the validation fails.
  """
    # `internal` is True when expanding internal realms (defined in a service
    # config file). Such realms can use internal roles and permissions and they
    # do not have implicit root bindings (since they are not associated with
    # any "project:<X>" identity used in implicit root bindings).
    internal = project_id == common.INTERNAL_PROJECT

    # The server code could have changed since the config passed the validation
    # and realms_cfg may not be valid anymore. Verify it still is. The code below
    # depends crucially on the validity of realms_cfg.
    validation.Validator(
        cfg_validation.Context.raise_on_error(),
        db,
        internal,
    ).validate(realms_cfg)

    # A lazily populated {role -> tuple of permissions} mapping.
    roles_expander = RolesExpander(db.roles, realms_cfg.custom_roles)
    # A helper to traverse the realms graph.
    realms_expander = RealmsExpander(roles_expander, realms_cfg.realms)

    # This creates @root realm and (optionally) extends it with implicit bindings.
    realms_expander.extend_root(
        db.implicit_root_bindings(project_id) if not internal else [])

    # Visit all realms and build preliminary bindings as pairs of
    # (a tuple with permission indexes, a list of principals who have them). The
    # bindings are preliminary since we don't know final permission indexes yet
    # and instead use some internal indexes as generated by RolesExpander. We
    # need to finish this first pass to gather the list of ALL used permissions,
    # so we can calculate final indexes. This is done inside of `roles_expander`.
    realms = []  # [(name, (permissions tuple, principals list))]
    for name in realms_expander.realm_names:
        # Build a mapping from a principal to the permissions set they have.
        principal_to_perms = collections.defaultdict(set)
        for principal, perms in realms_expander.per_principal_bindings(name):
            principal_to_perms[principal].update(perms)
        # Combine entries with the same set of permissions into one.
        perms_to_principals = collections.defaultdict(list)
        for principal, perms in principal_to_perms.items():
            perms_to_principals[tuple(sorted(perms))].append(principal)
        realms.append((name, perms_to_principals.items()))

    # We now know all permissions ever used by all realms. Convert them into the
    # form suitable for realm_pb2 by sorting alphabetically. Keep the mapping
    # between old and new indexes, to be able to change indexes in permission
    # tuples we stored in `realms`.
    perms, index_map = roles_expander.sorted_permissions()

    # Build the final sorted form of all realms by relabeling permissions
    # according to the index_map and by sorting stuff.
    return realms_pb2.Realms(
        permissions=[realms_pb2.Permission(name=p) for p in perms],
        realms=[
            realms_pb2.Realm(
                name='%s:%s' % (project_id, name),
                bindings=to_normalized_bindings(perms_to_principals,
                                                index_map),
                data=realms_expander.realm_data(name),
            ) for name, perms_to_principals in realms
        ])
 def test_realms_serialization(self):
     """Serializing snapshot with non-trivial realms configs."""
     realms_globals = model.AuthRealmsGlobals(
         key=model.realms_globals_key(),
         permissions=[
             realms_pb2.Permission(name='luci.dev.p1'),
             realms_pb2.Permission(name='luci.dev.p2'),
         ],
     )
     p1 = model.AuthProjectRealms(
         key=model.project_realms_key('proj1'),
         realms=realms_pb2.Realms(
             permissions=[{
                 'name': 'luci.dev.p2'
             }],
             realms=[{
                 'name':
                 'proj1:@root',
                 'bindings': [
                     {
                         'permissions': [0],
                         'principals': ['group:gr1'],
                     },
                 ],
             }],
         ),
     )
     p2 = model.AuthProjectRealms(
         key=model.project_realms_key('proj2'),
         realms=realms_pb2.Realms(
             permissions=[{
                 'name': 'luci.dev.p1'
             }],
             realms=[{
                 'name':
                 'proj2:@root',
                 'bindings': [
                     {
                         'permissions': [0],
                         'principals': ['group:gr2'],
                     },
                 ],
             }],
         ),
     )
     auth_db = make_auth_db_proto(realms_globals=realms_globals,
                                  project_realms=[p1, p2])
     self.assertEqual(
         auth_db.realms,
         realms_pb2.Realms(
             api_version=realms.API_VERSION,
             permissions=[{
                 'name': 'luci.dev.p1'
             }, {
                 'name': 'luci.dev.p2'
             }],
             realms=[
                 {
                     'name':
                     'proj1:@root',
                     'bindings': [
                         {
                             'permissions': [1],
                             'principals': ['group:gr1'],
                         },
                     ],
                 },
                 {
                     'name':
                     'proj2:@root',
                     'bindings': [
                         {
                             'permissions': [0],
                             'principals': ['group:gr2'],
                         },
                     ],
                 },
             ],
         ))
    def test_non_empty(self):
        self.mock_now(datetime.datetime(2014, 1, 1, 1, 1, 1))

        state = model.AuthReplicationState(key=model.replication_state_key(),
                                           primary_id='blah',
                                           primary_url='https://blah',
                                           auth_db_rev=123)
        state.put()

        global_config = model.AuthGlobalConfig(
            key=model.root_key(),
            modified_ts=utils.utcnow(),
            modified_by=model.Identity.from_bytes('user:[email protected]'),
            oauth_client_id='oauth_client_id',
            oauth_client_secret='oauth_client_secret',
            oauth_additional_client_ids=['a', 'b'],
            token_server_url='https://token-server',
            security_config='security config blob')
        global_config.put()

        group = model.AuthGroup(
            key=model.group_key('Some group'),
            members=[model.Identity.from_bytes('user:[email protected]')],
            globs=[model.IdentityGlob.from_bytes('user:*@example.com')],
            nested=[],
            description='Some description',
            owners='owning-group',
            created_ts=utils.utcnow(),
            created_by=model.Identity.from_bytes('user:[email protected]'),
            modified_ts=utils.utcnow(),
            modified_by=model.Identity.from_bytes('user:[email protected]'))
        group.put()

        another = model.AuthGroup(key=model.group_key('Another group'),
                                  nested=['Some group'])
        another.put()

        ip_whitelist = model.AuthIPWhitelist(
            key=model.ip_whitelist_key('bots'),
            subnets=['127.0.0.1/32'],
            description='Some description',
            created_ts=utils.utcnow(),
            created_by=model.Identity.from_bytes('user:[email protected]'),
            modified_ts=utils.utcnow(),
            modified_by=model.Identity.from_bytes('user:[email protected]'))
        ip_whitelist.put()

        ip_whitelist_assignments = model.AuthIPWhitelistAssignments(
            key=model.ip_whitelist_assignments_key(),
            modified_ts=utils.utcnow(),
            modified_by=model.Identity.from_bytes('user:[email protected]'),
            assignments=[
                model.AuthIPWhitelistAssignments.Assignment(
                    identity=model.Identity.from_bytes(
                        'user:[email protected]'),
                    ip_whitelist='bots',
                    comment='some comment',
                    created_ts=utils.utcnow(),
                    created_by=model.Identity.from_bytes(
                        'user:[email protected]')),
            ])
        ip_whitelist_assignments.put()

        realms_globals = model.AuthRealmsGlobals(
            key=model.realms_globals_key(),
            permissions=[
                realms_pb2.Permission(name='luci.dev.p1'),
                realms_pb2.Permission(name='luci.dev.p2'),
            ])
        realms_globals.put()

        model.AuthProjectRealms(key=model.project_realms_key('proj_id1'),
                                realms=realms_pb2.Realms(api_version=1234),
                                config_rev='rev1',
                                perms_rev='rev1').put()
        model.AuthProjectRealms(key=model.project_realms_key('proj_id2'),
                                realms=realms_pb2.Realms(api_version=1234),
                                config_rev='rev2',
                                perms_rev='rev2').put()

        captured_state, snapshot = replication.new_auth_db_snapshot()

        expected_state = {
            'auth_db_rev': 123,
            'modified_ts': datetime.datetime(2014, 1, 1, 1, 1, 1),
            'primary_id': u'blah',
            'primary_url': u'https://blah',
            'shard_ids': [],
        }
        self.assertEqual(expected_state, captured_state.to_dict())

        expected_snapshot = {
            'global_config': {
                '__id__':
                'root',
                '__parent__':
                None,
                'auth_db_rev':
                None,
                'auth_db_prev_rev':
                None,
                'modified_by':
                model.Identity(kind='user', name='*****@*****.**'),
                'modified_ts':
                datetime.datetime(2014, 1, 1, 1, 1, 1),
                'oauth_additional_client_ids': [u'a', u'b'],
                'oauth_client_id':
                u'oauth_client_id',
                'oauth_client_secret':
                u'oauth_client_secret',
                'security_config':
                'security config blob',
                'token_server_url':
                u'https://token-server',
            },
            'groups': [
                {
                    '__id__': 'Another group',
                    '__parent__': ndb.Key('AuthGlobalConfig', 'root'),
                    'auth_db_rev': None,
                    'auth_db_prev_rev': None,
                    'created_by': None,
                    'created_ts': None,
                    'description': u'',
                    'globs': [],
                    'members': [],
                    'modified_by': None,
                    'modified_ts': None,
                    'nested': [u'Some group'],
                    'owners': u'administrators',
                },
                {
                    '__id__':
                    'Some group',
                    '__parent__':
                    ndb.Key('AuthGlobalConfig', 'root'),
                    'auth_db_rev':
                    None,
                    'auth_db_prev_rev':
                    None,
                    'created_by':
                    model.Identity(kind='user', name='*****@*****.**'),
                    'created_ts':
                    datetime.datetime(2014, 1, 1, 1, 1, 1),
                    'description':
                    u'Some description',
                    'globs':
                    [model.IdentityGlob(kind='user', pattern='*@example.com')],
                    'members':
                    [model.Identity(kind='user', name='*****@*****.**')],
                    'modified_by':
                    model.Identity(kind='user', name='*****@*****.**'),
                    'modified_ts':
                    datetime.datetime(2014, 1, 1, 1, 1, 1),
                    'nested': [],
                    'owners':
                    u'owning-group',
                },
            ],
            'ip_whitelists': [
                {
                    '__id__':
                    'bots',
                    '__parent__':
                    ndb.Key('AuthGlobalConfig', 'root'),
                    'auth_db_rev':
                    None,
                    'auth_db_prev_rev':
                    None,
                    'created_by':
                    model.Identity(kind='user', name='*****@*****.**'),
                    'created_ts':
                    datetime.datetime(2014, 1, 1, 1, 1, 1),
                    'description':
                    u'Some description',
                    'modified_by':
                    model.Identity(kind='user', name='*****@*****.**'),
                    'modified_ts':
                    datetime.datetime(2014, 1, 1, 1, 1, 1),
                    'subnets': [u'127.0.0.1/32'],
                },
            ],
            'ip_whitelist_assignments': {
                '__id__':
                'default',
                '__parent__':
                ndb.Key('AuthGlobalConfig', 'root'),
                'assignments': [
                    {
                        'comment':
                        u'some comment',
                        'created_by':
                        model.Identity(kind='user',
                                       name='*****@*****.**'),
                        'created_ts':
                        datetime.datetime(2014, 1, 1, 1, 1, 1),
                        'identity':
                        model.Identity(kind='user',
                                       name='*****@*****.**'),
                        'ip_whitelist':
                        u'bots',
                    },
                ],
                'auth_db_rev':
                None,
                'auth_db_prev_rev':
                None,
                'modified_by':
                model.Identity(kind='user', name='*****@*****.**'),
                'modified_ts':
                datetime.datetime(2014, 1, 1, 1, 1, 1),
            },
            'realms_globals': {
                '__id__':
                'globals',
                '__parent__':
                ndb.Key('AuthGlobalConfig', 'root'),
                'auth_db_prev_rev':
                None,
                'auth_db_rev':
                None,
                'modified_by':
                None,
                'modified_ts':
                None,
                'permissions': [
                    realms_pb2.Permission(name='luci.dev.p1'),
                    realms_pb2.Permission(name='luci.dev.p2'),
                ],
            },
            'project_realms': [{
                '__id__':
                'proj_id1',
                '__parent__':
                ndb.Key('AuthGlobalConfig', 'root'),
                'auth_db_prev_rev':
                None,
                'auth_db_rev':
                None,
                'config_rev':
                u'rev1',
                'perms_rev':
                u'rev1',
                'modified_by':
                None,
                'modified_ts':
                None,
                'realms':
                realms_pb2.Realms(api_version=1234),
            }, {
                '__id__':
                'proj_id2',
                '__parent__':
                ndb.Key('AuthGlobalConfig', 'root'),
                'auth_db_prev_rev':
                None,
                'auth_db_rev':
                None,
                'config_rev':
                u'rev2',
                'perms_rev':
                u'rev2',
                'modified_by':
                None,
                'modified_ts':
                None,
                'realms':
                realms_pb2.Realms(api_version=1234),
            }],
        }
        self.assertEqual(expected_snapshot, snapshot_to_dict(snapshot))