User Tools

Site Tools


new_permission_model

This is an old revision of the document!


New Permission Model

(See also 8)

Goals

  • Allow non-owners to make changes
  • Allow individual “clients” access to different sets of photos
  • Allow finer-grained control over original images

This permission model will be group-based; individual users will have a personal “group”, but can exist in any number of groups. “Guests” will have their own special group, and permissions can be assigned to them like any other group.

By default, no groups will have permission to do anything, other than the administrator, who has full access to everything. Permissions are explicitly granted on an image-by-image basis, but if no specific permission is set on the image, the folder/album's default permissions are used instead.

This allows “group ownership” of images, folders, etc, and allows for individual user control while allowing them to share a common set of images.

Schema

create sequence groups_sequence start 0;

create table groups (
      identifier integer not null primary key,
      owner_id references users(identifier), -- who owns this group?
      description text,
      password text,                         -- group password, if any.
      date_added timestamp without time zone
);

create table group_memberships (
      group_id integer not null references groups(identifier),
      user integer not null references users(identifier),
      date_added timestamp without time zone
);

create table photo_permissions (
      photo integer not null references photo(identifier),
      group_id integer not null references groups(identifier),
      -- basic permissions per-image
      view boolean not null, -- aka thumbnail/preview & basic info
      details boolean not null, -- view all detailed info on image
      original boolean not null, -- access original
      edit boolean not null, -- modify 
      delete boolean nut null -- remove
);

create table folder_permissions (
      folder integer not null references folder(identifier),
      group_id integer not null references groups(identifier),
      -- default image permissions for this folder
      view boolean not null, -- aka thumbnail/preview & basic info
      details boolean not null, -- view all detailed info on image
      original boolean not null, -- access original
      edit boolean not null, -- modify 
      delete boolean nut null, -- remove
      -- folder permissions
      list boolean not null, -- display folder & view photo list
      modify boolean not null -- add/remove entries, (photos and/or subfolders)
);

create table album_permissions (
      album integer not null references album(identifier),
      group_id integer not null references groups(identifier),
      -- default image permissions for this folder
      view boolean not null, -- aka thumbnail/preview & basic info
      details boolean not null, -- view all detailed info on image
      original boolean not null, -- access original
      edit boolean not null, -- modify 
      delete boolean nut null, -- remove
      -- album permissions
      list boolean not null, -- display album & view photo list
      modify boolean not null -- add/remove entries, (photos and/or subalbums)
);

alter table photo add owner integer references groups(identifier);
alter table folder add owner integer references groups(identifier);
alter table album add owner integer references groups(identifier);

-- post-migration we need to:
drop table client;
drop table client_status;
alter table photo drop users;
alter table folder drop users;
alter table album drop users;
alter table photo alter column owner set not null;
alter table folder alter column owner set not null;
alter table album alter column owner set not null;
alter table photo drop hide_original;
alter table photo drop access_rights;
alter table folder drop access_rights;
alter table album drop access_rights;
alter table users drop type;
drop table user_type;
drop sequence user_type_sequence;

-- Don't forget about camera/film/filter/flash/support/scanner/location/clients
-- as they also have user/owners..
-- We also need Indices!

Migration

The goal of this migration is to transform all existing permissions to the new model.

Set things up
  • Create new tables. (see above)
  • Create group 0, “Admin Users”, and populate it with all admin users.
INSERT INTO groups(identifier, owner_id, description)  
     VALUES (0, 0 , 'Administrators');
INSERT INTO group_memberships(group_id, user)
     SELECT $_admin_grp_id as group_id, u.identifier
       FROM users u, user_type t
      WHERE u.type = t.identifier
        AND t.value = 'Administrator';
  • Create group 1, “Guest”
INSERT INTO groups(identifier, owner_id, description)  
     VALUES (1, 0, 'Guests');
  • Create group 2, “Registered Users”.
INSERT INTO groups(identifier, owner_id, description)  
     VALUES (2, 2, 'Registered Users');
The following list needs to be repeated for each user
  • Create a new group of the same name, make only that user a member.
$_grp_id = SELECT nextval(groups_sequence);

INSERT INTO groups(identifier, owner_id, description)  
     VALUES ($grp_id, $grp_id ,'$_userid's group');
INSERT INTO group_memberships(group, user) 
     VALUES ($_new_grpid, $_userid);
  • Update ownerships to point to the new group:
UPDATE folder f 
   SET f.owner = (SELECT g.group_id 
                    FROM group_memberships g 
                   WHERE g.user = f.users) ,
 WHERE f.users = $_userid;
      
  UPDATE album a
   SET a.owner = (SELECT g.group_id 
                    FROM group_memberships g 
                   WHERE g.user = a.users) ,
 WHERE a.users = $_userid;

  UPDATE photo p
   SET p.owner = (SELECT g.group_id 
                    FROM group_memberships g 
                   WHERE g.user = p.users) ,
 WHERE p.users = $_userid;  
  • Add user into the “Registered Users” group.
INSERT INTO group_memberships(group_id, user) 
     VALUES ($_reg_users_grpid, $_userid);
  • Create a new group called “$_userid's clients”, make all of their clients members.
INSERT INTO groups(identifier, description)  
     VALUES (SELECT nextval(groups_sequence),'$_userid's clients');
INSERT INTO group_memberships(group_id, user)  
     SELECT $_userid_clients_grpid as group_id, c.client
       FROM clients c
      WHERE c.users = $_userid;
For each folder
  • Add row granting owner all rights, including defaults:
INSERT INTO folder_permissions (folder, group_id, view, details, original, edit, delete, list, modify)
     VALUES ($_folderid, $_usergrp, 't', 't', 't', 't', 't', 't', 't');
  • if access_rights is private, stop here.
  • if access_rights is protected (ie clients-only)
INSERT INTO folder_permissions (folder, group_id, view, details, original, edit, delete, list, modify)
     VALUES ($_folderid, $_user_client_grp, 't', 't', 'f', 'f', 'f', 't', 'f'); 
  • if access_rights is public (ie everyone)
INSERT INTO folder_permissions (folder, group_id, view, details, original, edit, delete, list, modify)
     VALUES ($_folderid, $_guest_grp, 't', 'f', 'f', 'f', 'f', 't', 'f'); 
For each album:
  • Add row granting owner all rights, including defaults:
INSERT INTO album_permissions (album, group_id, view, details, original, edit, delete, list, modify)
     VALUES ($_albumid, $_usergrp, 't', 't', 't', 't', 't', 't', 't');
  • if access_rights is private, stop here.
  • if access_rights is protected (ie clients-only)
INSERT INTO album_permissions (album, group_id, view, details, original, edit, delete, list, modify)
     VALUES ($_albumid, $_user_client_grp, 't', 't', 'f', 'f', 'f', 't', 'f'); 
  • if access_rights is public (ie everyone)
INSERT INTO album_permissions (album, group_id, view, details, original, edit, delete, list, modify)
     VALUES ($_albumid, $_guest_grp, 't', 'f', 'f', 'f', 'f', 't', 'f'); 
For each photo:
  • Add row granting owner all rights.
INSERT INTO photo_permissions (photo, group_id, view, details, original, edit, delete)
     VALUES ($_photoid, $_usergrp, 't', 't', 't', 't', 't');
  • If access_rights is private, stop here.
  • If access_rights is protected (ie clients only):
INSERT INTO photo_permissions (photo, group_id, view, details, original, edit, delete)
     VALUES ($_photoid, $_user_client_grp, 't', 't', !$_hide_original, 'f', 'f');

Note; we may want to forego this step if the photo implicitly has identical permissions as we're granting

  • If access_rights is public (ie guest access):
INSERT INTO photo_permissions (photo, group_id, view, details, original, edit, delete)
     VALUES ($_photoid, $_guest_grp, 't', 'f', !$_hide_original, 'f', 'f');

Note; we may want to forego this step if the photo implicitly has identical permissions as we're granting.

Note2: The hide_original problem 8 79 makes this part difficult; existing behaivor says that “if hide_original is false, everyone who can view the preview can download the original.” but the help text says “allow for authorized users” but there's no mechanism for saying who those users are.

Finally
  1. Drop all necessary columns from the database (see above)
  2. ???
  3. Profit

Psuedocode

This code is roughly what we need to do to see if an image has the appropriate permissions. Before it goes into production it'll need to be implemented on the database using pl/pgsql, but the initial pass can be written in PHP.

// type == photo, group, album
// identifier == photo#, group#, album#
// user == userid
// permission == view/original/edit/delete details/list/modify
// returns true if match found or null if no match found.
function detail_permission(user, type, identifier, permission) {
      "SELECT p.$permission
        FROM $type_permissions p,
       WHERE p.$type = $identifier
         AND p.group_id in (SELECT g.group_id from group_memberships g
                            WHERE g.user = $user);"

// Ideally we would return the "best" hit, true or false, but the problem with that 
// is that if G1 == NULL but G2 == false, we'd return false and consequently deny access
// even though there may be a "default true" value for G1 elsewhere.

      $best = null;
      foreach ($results as $result) {
              if ($result == true) {
                      return $result;
              }
      }
      return null;
}

// view == "folder", "album", "photo", "search", etc...
function permission(user, photo, permission, view) {
      // members of admin group get all permissions implicitly.
      if (user_in_admin_grp($user))  return TRUE;

      // explicit permission on the photo trumps everything else.
      $stored = detail_permission($user, 'photo',
                                  $photo, $permission);
      if ($stored != null)
              return $stored;

      if ($view != 'album') {
              $folder = folder_for_photo($identifier);
              // make the lookup deal with parents too.
              while($folder != null) {
                 $stored = detail_permission($user, 'folder',
                                             $folder, $permission);
                 if ($view == 'folder' && $stored != null) {
                         return $stored;
                 }
                 if ($stored == true) return true;

                 $folder = parent_for_folder($folder);
              }
      }

      if (view != 'folder') {
              foreach (albums_for_photo($photo) as $album) {
                      while ($album != null) {
                         $auth = detail_permission($user, 'album',
                                                   $album, $permission);
                         if ($auth != null) {
                                 if ($auth == true) return true;
                                 $stored = $auth;
                         }
                         $album = parent_for_album($album);
                      }
              }
      }

      return false;  // no "allowed" permission found, we have to deny it.
}

Source Hax0r (in rough order)

  1. Schema finalization
  2. Migration code for installer
  3. Port admin test code to new backend
  4. Write PL/pgsql permission lookup code
  5. Create notion of “permission sets”, defined per-user.
  6. Define new preferences (is_admin, user_priv_group, user_client_grp, default_permissions, more?)
  7. Port account registration to new backend (auto-create groups, etc)
  8. Port photo view/description pages to new backend.
  9. Port folder/album/search/print/export/spool/etc (photo listing) pages to new backend.
  10. Port folder/album add/edit/delete pages to new backend. (don't forget password-protection!)
  11. Port photo import to new backend.
  12. Create Group UI elements
  13. Create Permission UI elements (for photo add and bulk update too!)
  14. Figure out 'client' mess, rework interaction to new backend.
  15. Port over equipment/profile pages. (yuck)
new_permission_model.1177004902.txt.gz · Last modified: 2007/04/19 17:48 by pizza