Sign In/My Account | View Cart  
advertisement


Listen Print Discuss

Elements of Access Control
by Vladi Belperchinov-Shabanski | Pages: 1, 2, 3

Run-Time

Applications apply access control after user login. You can combine it with the login procedure--for example to allow only specific group of users to connect on weekends. Even so, the access check occurs only after the login succeeds, that is, when the username and password are correct.

A simple approach for loading required access info is:

  • Login, check username and password
  • For unsuccessful login, deny access, print message, etc.
  • For successful login, load group list for the user from database
  • Check for required group(s) for login

    This may deny login, print a message, or continue.

  • User logged in, continue

    All access checks for operations happen after this point.

The run-time storage for a user's groups can be simple hash. It can be either global or inside the user session object, depending on your system design. I've used a global hash here for simplicity of the examples, but if you copy and paste this code, remember that it is mandatory for you to clear and recreate this global hash for every request right after the login or user session changes! You can also use some kind of session object to drop all user data at the end of the session, but this is just an option, not the only correct or possible way.

(Also, a truly robust system would store a well-hashed version of the password, not the literal password, but that's a story for a different article.)

  #!/usr/bin/perl
  use strict;
  use DBI;
  use Data::Dumper;

  our $USER_NAME;
  our $USER_ID;
  our %USER_GROUPS;

  my $DBH = DBI->connect( "dbi:Pg:dbname=letme", "postgres", "",
      { AutoCommit => 0 } );

  # this is just an example!
  # username and password acquiring depends on the specific application
  user_login( 'Damian', 'secret4' );

  print "User logged in: $USER_NAME\n";
  print "User id:        $USER_ID\n";
  print "User groups:    " . join( ', ', keys %USER_GROUPS ) . "\n";

  sub user_login
  {
    my $user_name = shift;
    my $user_pass = shift;

    $USER_NAME   = undef;
    $USER_ID     = undef;
    %USER_GROUPS = ();

    # both name and password are required
    die "Empty user name"     if $user_name eq '';
    die "Empty user password" if $user_pass eq '';

    eval
    {
      my $ar = $DBH->selectcol_arrayref(
          'SELECT ID FROM USER WHERE NAME = ? AND PASS = ?',
                                        {},
                                        $user_name, $user_pass );

      $USER_ID   = shift @$ar;

      die "Wrong user name or password" unless $USER_ID > 0;

      $USER_NAME = $user_name;

      # loading groups
      my $ar = $DBH->selectcol_arrayref( 'SELECT GROUP FROM MAP WHERE USER = ?',
                                        {},
                                        $USER_ID );

      %USER_GROUPS = map { $_ => 1 } @$ar;
    };
    if( $@ )
    {
      # something failed, it is important to clear user data here
      $USER_NAME   = undef;
      $USER_ID     = undef;
      %USER_GROUPS = ();

      # propagate error
      die $@;
    }
  }

If Damian's password is correct, this code will print:

  User logged in: Damian
  User id:        1
  User groups:    1, 2

The group access check function now is even simpler:

  sub check_access
  {
    my $group = shift;
    return 0 unless $group > 0;
    return $USER_GROUPS{ $group };
  }

Sample code for an access check after login will be something like:

  sub edit_data
  {
    # require user to be in group 1 (admin) to edit data...
    die "Access denied" unless check_access( 1 );

    # user allowed, group 1 check successful
    ...
  }

or

  if( check_access( 1 ) )
  {
    # user ok
  }
  else
  {
    # access denied
  }

Access Instructions

The next problem is how to define which groups can perform specific operations. Where this information is static (most cases), you can store group lists in configuration (text) files:

  LOGIN: 2
  EDIT:  1

That is, the EDIT operation needs group 1 (admin) and LOGIN needs group 2 (all users).

Another example is to allow only administrators to log in during weekends:

  # all users for mon-fri
  LOGIN_WEEKDAYS: 2

  # only admin for sat-sun
  LOGIN_WEEKENDS: 1

Administrators will be in both groups (1, 2), so they will be able to log in anytime. All regular users cannot login on weekends.

This group list includes a moderators group. It could be useful to allow moderators do their job on weekends as well, implying an OR operation:

  # only admin or moderators for sat-sun
  LOGIN_WEEKENDS: 1, 3

This named set of groups is a policy.

For now, there's only one level in the policy and an OR operation between groups in a list. Real-world policies may be more complex. However there is no need to overdesign this. Even large systems may work with just one more level. Here's an AND operation:

  LOGIN_WEEKENDS: 1+3, 4, 1+5+9

This policy will match (allowing login on weekend days) only for users in the following groups:

     1 AND 3
  OR 4
  OR 1 AND 5 AND 9

Pages: 1, 2, 3

Next Pagearrow