Sign In/My Account | View Cart  
advertisement


Listen Print Discuss

Elements of Access Control

by Vladi Belperchinov-Shabanski
February 13, 2008

Why Gates?

In a perfect world we wouldn't do things we should not. However the world is not like this; people do forbidden things sometimes. This also applies to computer systems used by more than one person. Almost everyone has tried to read someone else's email, view accounting department salary reports, or something else, or access otherwise hidden data.

I know you have never done this, but many people have.

In Construction

The simplest way to allow or forbid a user account to do something is to check if the account is in a list of permitted accounts somewhere. If you assume that everything is forbidden unless explicitly allowed, the access function can be as simple as:

  # access_check() return 1 or undef
  sub access_check
  {
    my $user_id     = shift;
    my @allow_users = @_;

    my %quick_allow = map { $_ => 1 } @allow_users;

    return $quick_allow{ $user_id };
  }

  my @allowed = ( 11, 12, 23, 45 );

  print "User 23 allowed\n" if access_check( 23, @allowed );
  print "User 13 allowed\n" if access_check( 13, @allowed );
  print "User 99 allowed\n" if access_check( 99, @allowed );

  # only "User 23 allowed" will be printed

Usually access control can be almost as simple as this function. Using user IDs for access control is simple, but tends to be hard to maintain. The problem appears with systems with many users or with public systems where new users may be created at any point. Access lists may become very large for each operation, which needs access controls.

One solution to this problem is access groups. Each user may be a member of several groups. The access check will pass if the user is a member of a group with permission for the required operation. This middle level in the access check isolates users from the access check directly. It also helps the system's design--you can associate preset access groups with all controlled operations at their point of creation. Subsequently created users only need to be attached to one or more of those groups:

  # mimic real system environment:
  # %ALL_USER_GROUPS represents "storage" that contains all
  # groups that each user is attached to
  my %ALL_USER_GROUPS = (
                    23 => [ qw( g1  g4 ) ],
                    13 => [ qw( g3  g5 ) ],
                    );
  # user 23 is in groups g1 and g4
  # user 13 -- in g3 and g5

  # return list of user's groups. read data from storage or
  # from %ALL_USER_GROUPS in this example
  sub get_user_groups
  {
    my $user_id     = shift;

    return @{ $ALL_USER_GROUPS{ $user_id } || [] };
  }

  # access_check_grp() return 1 or 0
  sub access_check_grp
  {
    my $user_id     = shift;
    my @allow_users = @_;

    my %quick_allow = map { $_ => 1 } @allow_users;

    my @user_groups = get_user_groups( $user_id );

    for my $group ( @user_groups )
    {
      # user groups is listed, allow
      return 1 if $quick_allow{ $group };
    }

    # user group not found, deny
    return 0;
  }

  # this groups list is static and will not be altered
  # when users are added or removed from the system
  my @allowed = qw( g1  g2  g7  g9 );

  print "User 23 allowed\n" if access_check_grp( 23, @allowed );
  print "User 13 allowed\n" if access_check_grp( 13, @allowed );
  print "User 99 allowed\n" if access_check_grp( 99, @allowed );

  # only "User 23 allowed" will be printed

Storage

Probably the most popular storage for system data nowadays is the SQL database. Here is a simple example of how to store users, groups, and mapping between them. Three tables are required:

  SQL CREATE statements:

  create table user  ( id integer primary key, name char(64), pass char(64) );
  create table group ( id integer primary key, name char(64) );
  create table map   ( user integer, group integer );

  TABLE USER:

   Column |     Type      | Modifiers
  --------+---------------+-----------
   id     | integer       | not null
   name   | character(64) |
   pass   | character(64) |

  TABLE GROUP:

   Column |     Type      | Modifiers
  --------+---------------+-----------
   id     | integer       | not null
   name   | character(64) |

  TABLE MAP:

   Column |  Type   | Modifiers
  --------+---------+-----------
   user   | integer |
   group  | integer |

Let's fill those tables with some data:

  letme=# select id, name from user;
   id |       name
  ----+------------------
    1 | Damian
    2 | Clive
    3 | Lana
  (3 rows)

  letme=# select * from group;
   id |       name
  ----+------------------
    1 | Admin
    2 | Users
    3 | Moderators
  (3 rows)

  letme=# select * from map;
   user | group
  -----+-----
     1 |   1
     1 |   2
     3 |   2
     3 |   3
     2 |   2
  (4 rows)

Users in this example are attached to those groups:

  Damian: Users, Admin
  Clive:  Users
  Lana:   Users, Moderators

Pages: 1, 2, 3

Next Pagearrow