Gyroscope / User Access Control
Gyroscope uses a cookie-based system to authenticate users. Each user is
described with a list of capabilities, which is then used by specific
application components.
In simple terms, here's what happens internally when user Bob signs in with
his password "bob123":
First,
login.php compares the hash of Bob's password against an internally
stored hash to determine whether the passwords match. Upon successful
authentication, a set of cookies are placed. One of these cookies indicates
Bob's capabilities, for instance "billing" and "reporting".
Bob's further interaction with the system no longer results in database
queries. The server side script utilises functions in auth.php to determine
whether Bob has already signed in and whether he has access to billing or
reporting.
To redirect a user to a login page if he hasn't signed in:
<?php
login();
To silently fail a user request:
<?php
login(1);
To obtain user info:
$user=userinfo();
$login=$user['login'];
$userid=$user['userid'];
$groups=$user['groups'];
To check the billing flag:
if ($user['groups']['billing']){
//billing specific code here
}
Note that the group names are arbitrary. They can be used to implement a
role-based access control system or clearance-level based security, or a
system that's fine grained such that a user can view and modify a record but
not delete it. It's up to the application developer to design a secure and
managable access control system.
Now that we know how to use authentication and access control in
Gyroscope, let's look at how the system handles to security issues that come
with cookie-based systems.
- Cookies can be forged
- Cookies can be stolen
- Cookies can be outdated
To prevent cookie forgery, we also store a validation hash as a cookie. By
default, this hash uses a salt that's defined in auth.php and contains the login,
userid and groups. If any of these values are tempered, the cookie will fail to
authenticate.
Starting Version 9.6, each service request is checked against a list of inactive users.
This allows immediate access denial to selected users.
Instant eviction can be optimized by uncommenting the memcache related lines in
evict_getblockedids in
evict.php
and
updateuser in
icl/updateuser.inc.php.
gsid and gsguard
Starting Version 12.5, access to records can be partitioned by "gs instances" that are identified by "gsid". It is the
developer's responsibility to ensure that the gsid field is in each table that corresponds to a base record. For example,
in a database of cars where a car is a base record, the
cars table should have a
gsid field. However, a subordinate table,
carparts, does not need a gsid.
The gsid can be retrieved from the authenticated user context:
$user=userinfo();
$gsid=$user['gsid']+0;
Base record tables can then be authenticated as such:
$query="select * from cars where ... and gsid=$gsid";
A utility function,
gsguard, can be used to regulate access to sub records.
Here is a trivial use case that ensures that the
carid in
carparts is linked to an authorised car record:
...
gsguard($carid,'cars','carid');
Internally, the following query is built to enforce access:
select cars.carid from cars where cars.gsid=$gsid and cars.carid=$carid
In a more complex scenario, where cars, carparts and partattributes form a 1-N chain, the entire chain can be authenticated:
...
gsguard(
$attrid,
array('cars','carparts','partattributes'),
array('carid-carid','partid-partid','attrid'),
);
The above function call effectively checks the credentials against the following query:
select cars.carid
from cars,carparts,partattributes
where cars.gsid=$gsid
and cars.carid=carparts.carid
and carparts.partid=partattributes.partid
and partattributes.attrid=$attrid
The
keys array contains field names of connecting keys, separated by dashes (-).
The last element of the
keys array is the exit field that matches the input parameter, typically obtained by GETVAL/QETVAL.