简体   繁体   中英

Building access matrix based on parent=>child relationship of objects?

Some background:

A game belongs to a round ,
A round belongs to a season ,
A season belongs to a competition
A competition is unowned.

We have eight tables

name => columns

`games`            => `id`, `round_id`,  
`rounds`           => `id`, `season_id`,  
`seasons`          => `id`, `competition_id`,  
`competitions`      => `id`,
----
`user_game`        => `user_id`, `game_id`, 
`user_round`       => `user_id`, `round_id`,
`user_season`      => `user_id`, `season_id`,
`user_competition` => `user_id`, `competition_id`

So, the first four tables link the different parts together,
the second four tables link a user to the respective part.

Some dummy data, note I've split the tables when the second id changes for easy reading.

First four tables

/--GAMES--------------\      /--ROUNDS-------------\
| id | round_id       |      | id | season_id      |
| 1  | 1              |      | 1  | 1              |
| 2  | 1              |      | 2  | 1              |
|----|----------------|      | 3  | 1              |
| 3  | 2              |      |----|----------------|
| 4  | 2              |      | 4  | 2              |
|----|----------------|      | 5  | 2              |
| 5  | 3              |      | 6  | 2              |
| 6  | 3              |      |----|----------------|
|----|----------------|      | 7  | 3              |
| 7  | 4              |      | 8  | 3              |
| 8  | 4              |      | 9  | 3              |
|----|----------------|      |----|----------------|
| 9  | 5              |      | 10 | 4              |
| 10 | 5              |      \---------------------/
|----|----------------|
| 11 | 6              |      /--SEASONS------------\
| 12 | 6              |      | id | competition_id |
|----|----------------|      | 1  | 1              |
| 13 | 7              |      | 2  | 1              |
| 14 | 7              |      |----|----------------|
|----|----------------|      | 3  | 2              |
| 15 | 8              |      | 4  | 2              |
| 16 | 8              |      \---------------------/
|----|----------------|
| 17 | 9              |      /--COMPETITIONS-------\
| 18 | 9              |      | id                  |
|----|----------------|      | 1                   |
| 19 | 10             |      | 2                   |
| 20 | 10             |      \---------------------/
\---------------------/ 

The next four tables are best explained in the list below

Users:

  • User 1
    • linked to just game 1:
      user_game (user_id:1, game_id:1)
    • has direct access on game 1
    • has parent access on round 1
    • has parent access on season 1
    • has parent access on competition 1
  • User 2
    • linked to just round 1:
      user_round (user_id:2, round_id:1)
    • has child access on games 1,2
    • has direct access on round 1
    • has parent access on season 1
    • has parent access on competition 1
  • User 3
    • linked to round 1:
      user_round (user_id:3, round_id:1)
    • has all of user 2 's access
    • linked to game 2:
      `user_game (user_id:3, game_id:2).
    • has direct access on game 2
    • also linked to game 13:
      user_game (user_id:3, game_id:13)
    • has direct access on game 13
    • has parent access on round 7
    • has parent access on season 3
    • has parent access on competition 2

So, when fetching access for the three users above I want to end up with these three arrays, Noting that:
parent_access : User has partial access as has access to a child object (regardless of what object)
direct_access : User has full access as has been granted directly
child access : User has full access as a parent object (regardless of what object) has been granted direct access

User 1

$user1 = array(
    'games' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => false,
            'direct_access' => true,
            'child_access'  => false
        )
    ),

    'rounds' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => true,
            'direct_access' => false,
            'child_access'  => false
        )
    ),

    'seasons' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => true,
            'direct_access' => false,
            'child_access'  => false
        ),
    ),

    'competitions' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => true,
            'direct_access' => false,
            'child_access'  => false
        ),
    )
);

User 2

$user2 = array(
    'games' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => false,
            'direct_access' => false,
            'child_access'  => true
        ),
        [2] => array(
            'id' => 2,
            'parent_access' => false,
            'direct_access' => false,
            'child_access'  => true
        )
    ),

    'rounds' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => false,
            'direct_access' => true,
            'child_access'  => false
        )
    ),

    'seasons' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => true,
            'direct_access' => false,
            'child_access'  => false
        ),
    ),

    'competitions' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => true,
            'direct_access' => false,
            'child_access'  => false
        ),
    )
);

User 3

$user3 = array(
    'games' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => false,
            'direct_access' => false,
            'child_access'  => true
        ),
        [2] => array(
            'id' => 2,
            'parent_access' => false,
            'direct_access' => true,
            'child_access'  => true
        ),
        [13] => array(
            'id' => 13,
            'parent_access' => false,
            'direct_access' => true,
            'child_access'  => false
        )
    ),

    'rounds' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => false,
            'direct_access' => true,
            'child_access'  => false
        ),
        [7] => array(
            'id' => 7,
            'parent_access' => true,
            'direct_access' => false,
            'child_access'  => false
        )
    ),

    'seasons' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => true,
            'direct_access' => false,
            'child_access'  => false
        ),
        [3] => array(
            'id' => 3,
            'parent_access' => true,
            'direct_access' => false,
            'child_access'  => false
        )
    ),

    'competitions' => array(
        [1] => array(
            'id' => 1,
            'parent_access' => true,
            'direct_access' => false,
            'child_access'  => false
        ),
        [2] => array(
            'id' => 2,
            'parent_access' => true,
            'direct_access' => false,
            'child_access'  => false
        )
    )
);

A bit different of an outcome, but this is posted here to show my current working, and it still does work as an answer, albeit one I am not happy with.

So, here is my current code: lines like: $competitions = $this->competitions->disabled(true)->getAll(); run the query on the competitions table and return a custom object, so things are a bit different there, but you should be able to work out what it does by the code.

What U don't like about this here is that we have 18 foreach loops! many nested so that's 18 re-running foreach loops! Can anyone see a way to reduce this?

public function access($user_id, $action = 'none')
{

    $access = array(
        'competitions' => array()
    );

    /* COMPETITIONS */
    $competitions = $this->competitions->disabled(true)->getAll();
    foreach ($competitions as $competition) {
        $access['competitions'][$competition->data('id')] = array(
            //'item' => $competition,
            'type' => 'competition',
            'id' => $competition->data('id'),
            'child_access' => false,
            'direct_access' => false,
            'parent_access' => false,
            'seasons' => array()
        );

        /* SEASONS */
        $seasons = $competition->seasons(true);
        foreach ($seasons as $season) {
            $access['competitions'][$competition->data('id')]['seasons'][$season->data('id')] = array(
                //'item' => $season,
                'type' => 'season',
                'id' => $season->data('id'),
                'child_access' => false,
                'direct_access' => false,
                'parent_access' => false,
                'rounds' => array()
            );

            /* ROUNDS */

            $rounds = $season->rounds(true);
            foreach ($rounds as $round) {
                $access['competitions'][$competition->data('id')]['seasons'][$season->data('id')]['rounds'][$round->data('id')] = array(
                    //'item' => $round,
                    'type' => 'round',
                    'id' => $round->data('id'),
                    'child_access' => false,
                    'direct_access' => false,
                    'parent_access' => false,
                    'games' => array()
                );

                /* GAMES */
                $games = $round->games(true);
                foreach ($games as $game) {
                    //dump('$access["competitions"]['.$competition->data('id').']["seasons"]['.$season->data('id').']["rounds"]['.$round->data('id').']["games"]['.$game->data('id').']');
                    $access['competitions'][$competition->data('id')]['seasons'][$season->data('id')]['rounds'][$round->data('id')]['games'][$game->data('id')] = array(
                        //'item' => $game,
                        'type' => 'game',
                        'id' => $game->data('id'),
                        'child_access' => false,
                        'direct_access' => false,
                        'parent_access' => false
                    );
                }

            }
        }
    }


    /* CHECK COMPETITIONS */
    $competitions = $this->db->select('competition_id')->from('user_competition')->where('user_id', $user_id)->get();
    foreach ($competitions->result() as $id) {
        $id = $id->competition_id;
        $access['competitions'][$id]['direct_access'] = true;

        /* SEASONS */
        foreach ($access['competitions'][$id]['seasons'] as &$season) {
            $season['child_access'] = true;

            /* ROUNDS */
            foreach ($season['rounds'] as &$round) {
                $round['child_access'] = true;

                /* GAMES */
                foreach ($round['games'] as &$game) {
                    $game['child_access'] = true;
                    unset($game);
                }

                unset($round);

            }
            unset($season);
        }
    }

    /* CHECK SEASONS */
    $seasons = $this->db->select('season_id')->from('user_season')->where('user_id', $user_id)->get();
    foreach ($seasons->result() as $id) {
        $id = $id->season_id;
        $competition_id = $this->seasons->disabled(true)->get($id)->data('competition id');

        $competition = $access['competitions'][$competition_id];
        $competition['parent_access'] = true;

        $season = $competition['seasons'][$id];
        $season['direct_access'] = true;

        /* ROUNDS */
        foreach ($season['rounds'] as &$round) {
            $round['child_access'] = true;

            /* GAMES */
            foreach ($round['games'] as &$game) {
                $game['child_access'] = true;
                unset($game);
            }
            unset($round);

        }
    }

    /* CHECK ROUNDS */
    $rounds = $this->db->select('round_id')->from('user_round')->where('user_id', $user_id)->get();
    foreach ($rounds->result() as $id) {
        $id = $id->round_id;
        $round_obj = $this->rounds->disabled(true)->get($id);
        $season_obj = $round_obj->season();
        $competition_id = $season_obj->data('competition id');

        $access['competitions'][$competition_id]['parent_access'] = true;

        $access['competitions'][$competition_id]['seasons'][$season_obj->data('id')]['parent_access'] = true;

        $access['competitions'][$competition_id]['seasons'][$season_obj->data('id')]['rounds'][$id]['direct_access'] = true;


        /* GAMES */

        foreach ($access['competitions'][$competition_id]['seasons'][$season_obj->data('id')]['rounds'][$id]['games'] as &$game) {
            $game['child_access'] = true;
            unset($game);
        }

    }

    /* CHECK GAMES */
    $games = $this->db->select('game_id')->from('user_game')->where('user_id', $user_id)->get();
    foreach ($games->result() as $id) {
        $id = $id->game_id;
        $game_obj = $this->games->disabled(true)->get($id);
        $round_obj = $game_obj->round();
        $season_obj = $round_obj->season();
        $competition_id = $season_obj->data('competition id');

        $access['competitions'][$competition_id]['parent_access'] = true;

        $access['competitions'][$competition_id]['seasons'][$season_obj->data('id')]['parent_access'] = true;

        $access['competitions'][$competition_id]['seasons'][$season_obj->data('id')]['rounds'][$round_obj->data('id')]['parent_access'] = true;
        $access['competitions'][$competition_id]['seasons'][$season_obj->data('id')]['rounds'][$round_obj->data('id')]['games'][$id]['direct_access'] = true;

    }

    if ($action == 'trim') {
        foreach ($access['competitions'] as $k => &$competition) {
            if ($competition['child_access'] === false &&
                $competition['direct_access'] === false &&
                $competition['parent_access'] === false
            ) {
                unset($access['competitions'][$k]);
                continue;
            }

            foreach ($competition['seasons'] as $k1 => &$season) {
                if ($season['child_access'] === false &&
                    $season['direct_access'] === false &&
                    $season['parent_access'] === false
                ) {
                    unset($competition['seasons'][$k1]);
                    continue;
                }
                foreach ($season['rounds'] as $k2 => &$round) {
                    if ($round['child_access'] === false &&
                        $round['direct_access'] === false &&
                        $round['parent_access'] === false
                    ) {
                        unset($season['rounds'][$k2]);
                        continue;
                    }

                    foreach ($round['games'] as $k3 => $game) {
                        if ($game['child_access'] === false &&
                            $game['direct_access'] === false &&
                            $game['parent_access'] === false
                        ) {
                            unset($round['games'][$k3]);
                            continue;
                        }
                    }
                }
            }
        }
    }

    dump($access);
}

You need to use an authorization language such as XACML to express who can access which particular data. Then, the tables you have become one source of "attributes".

With XACML you can express rules such as:

  • a user can view a game if that user is taking part in that game
  • a user can view a game if that user can view the round the game belongs to (parent-child relationship)...

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM