midcom_services_authMain Authentication/Authorization service class, it provides means to authenticate users and to check for permissions.
Unless further qualified, "Midgard Content Objects" can be either pre-mgdschema or mgdschema objects. The only neccessary constraint is that a MidCOM base class (midgard_baseclasses_database_*) must be available.
This implementation is based on the general idea outlined in mRFC 15 ( http://www.midgard-project.org/development/mrfc/0015.html ), MidCOM Authentication and Access Control service. Developers Note: Be aware that the basic requirements for the ACL system undergone a major chance during the implementation, as the DBA layer with full access control even for database I/O was added. The proposals from the mRFC are largly outdated therefore. What is documented on the main MidCOM documentation has to take priority obviously.
Privilege definition
Privileges are represented by the class midcom_core_privilege and basically consist of three parts: Name, Assignee and Value:
The privilege name is an unique identifier for the privilege. The mRFC 15 defines the syntax to be $component:$name, where $component is either the name of the component or one of 'midgard' or 'midcom' for core privileges. Valid privilege names are for example 'de.linkm.taviewer:do_something' or 'midgard:update'.
The assignee is the entity to which the privilege applies, this can be one of several things, depending on where the privilege is taken into effect, I'll explain this below in more detail:
On content objects (generally every object in the system used during 'normal operation'):
How are privileges read and merged
First, you have to understand, that there are actually three disctinct sources where a privilege comes from: The systemwide defaults, the currently authenticated user and the content object which is being operated on. We'll look into this distinction first, before we get on to the order in which they are merged.
Systemwide default privileges
This is analogous to the MidCOM default configuration, they are taken into account globally to each and every check wether a privilege is granted. Whenever a privilege is defined, there is also a default value (either ALLOW or DENY) assigned to it. They serve as a basis for all privilege sets and ensure, that there is a value set for all privileges.
These defaults are defined by the MidCOM core and the components respectivly and are very restrictive, basically granting read-only access to all non sensitive information.
Currently, there is no way to influence these privileges unless you are a developer and writing new components.
Class specific, systemwide default privileges (for magic assignees only)
Often you want to have a number of default privileges for certain classes in general. For regular users/groups you can easily assign them to the corresponding users/groups, there is one special case which cannot be covered there at this time: You cannot set defaults applicable for the magic assignees EVERYONE, USERS and ANONYMOUS. This is normally only of interest for component authors, which want to have some special privileges assigned for their obejcts, where the global defaults do no longer suffice.
These privileges are queried using a static callback of the DBA classes in question, see the following example:
- function get_class_magic_default_privileges()
- {
- return Array (
- 'EVERYONE' => Array(),
- 'ANONYMOUS' => Array(),
- 'USERS' => Array('midcom:create' => MIDCOM_PRIVILEGE_ALLOW)
- );
- }
See also the documentation of the $_default_magic_class_privileges member for further details.
User / Group specific privileges
This kind of privileges are rights, assigned directly to a user. Similar to the systemwide defaults, they too apply to any operation done by the user / group respectivly throughout the system. The magic assignee SELF is used to denote such privileges, which can obviously only be assigned to users or groups. These privileges are loaded at the time of user authentication only.
You should use these privileges carefully, due to their global nature. If you assign the privilege midgard:delete to a user, this means that the user can now delete all objects he can read, unless there are again restricting privileges set to content objects.
To be more flexible in the control over the top level objects, you may add a classname which restricts the validity of the privilege to a class and all of its decendants.
Content object privileges
This is the kind of privilege that will be used most often. They are accociated with any content object in the system, and are read on every access to a content object. As you can see in the introduction, you have the most flexibility here.
The basic idea is, that you can assign privileges based on the combination of users/groups and content objects. In other words, you can say The user x has the privilege midgard:update for this object (and its decendants) only. This works with (virtual) groups as well.
The possible assignees here are either a user, a group or one of the magic assignees EVERYONE, USERS or ANONYMOuS, as outlined above.
Be aware, that Midgard Persons and Groups count as content object when loaded from the database in a tool like net.nemein.personell, as the groups are not used for authentication but for regular site operation there. Therefore, the SELF privileges mentioned above are not taken into account when determining the content object privileges!
Privilege merging
This is, where we get to the guts of privileges, as this is not trivial (but nevertheless straight-forward I hope). The general idea is based on the scope of object a privilege applies:
System default privileges obviously have the largest scope, they apply to everyone. The next smaller scope are privileges which are assigned to groups in general, followed by privileges assigned directly to a user.
From this point on, the privileges of the content objects are next in line, starting at the top-level objects again (for example a root topic). The smallest scope finally then has the object that is being accessed itself.
Let us visualize this a bit:
^ larger scope System default privileges | Class specific magic assignee default privileges | Root Midgard group | ... more parent Midgard groups ... | Direct Midgard group membership | Virtual group memberships | User | SELF privileges limited to a class | Root content object | ... more parent objects ... v smaller scope Accessed content object
Privileges assigned to a specific user always override owner privileges; owner privileges are calculated on a per-content-object bases, and are merged just before the final user privileges are merged into the privilege set. It is of no importance from where you get ownership at that point.
Implementation notes: Internally, MidCOM separates the "user privilege set" which is everything down to the line User above, and the content object privileges, which constitutes of the rest. This separation has been done for performance reasons, as the user's privileges are loade immediately upon authentication of the user, and the privileges of the actual content objects are merged into this set then. Normally, this should be of no importance for ACL users, but it explains the more complex graph in the original mRFC.
Predefined Privileges
The MidCOM core defines a set of core privileges, which fall in two categories:
Midgard Core Privileges
These privileges are part of the MidCOM Database Abstraction layer (MidCOM DBA) and have been originally proposed by me in a mail to the Midgard developers list. They will move into the core level eventually, but for the time being MidCOM will control them. Unless otherwise noted, all privileges are denied by default and no difference between owner and normal default privileges is made.
Assigning Privileges
You assign priviliges by using the DBA set_privilege method, whose static implementation can be found in midcom_baseclasses_core_dbobject::set_privilege(). Here is a quick overview over that API, which naturally only works on MidCOM DBA level objects:
Array get_privileges(); midcom_core_privilege create_new_privilege_object($name, $assignee = null, $value = MIDCOM_PRIVILEGE_ALLOW) bool set_privilege(midcom_core_privilege $privilege); bool unset_all_privileges(); bool unset_privilege(midcom_core_privilege $privilege);
These calls operate only on the privileges of the given object. They do not do any merging whatsoever, this is the job of the auth framework itself (midcom_services_auth).
Unsetting a privilege does not deny it, but clears the privilege specification on the current object and sets it to INHERIT internally. As you might have guessed, if you want to clear all privileges on a given object, call unset_all_privileges() on the DBA object in question.
See the documentation of the DBA layer for more information on these five calls.
Checking Privileges
This class overs various methods to verify the privilege state of a user, all of them prefixed with can_* for privileges and is_* for membership checks.
Each function is available in a simple check version, which returns true or false, and a require_* prefixed variant, which has no return value. The require variants of these calls instead check if the given condition is met, if yes, they return silently, otherwise they throw an access denied error.
Authentication
TODO: Fully document authentication.
Whenever the system successfully creates a new login session (during auth service startup), it checks whether the key midcom_services_auth_login_success_url is present in the HTTP Request data. If this is the case, it relocates to the URL given in it. This member isn't set by default in the MidCOM core, it is intended for custom authentication forms. The MidCOM relocate function is used to for relocation, thus you can take full advantage of the convenience functions in there. See midcom_application::relocate() for details.
Located in /midcom.core/midcom/services/auth.php (line 284)
PEAR | --midcom_baseclasses_core_object | --midcom_services_auth
bool
$admin
= false (line 305)
Admin user level state. This is true if the currently authenticated user is an Midgard Administrator, false otherwise.
This effectivly maps to $_MIDGARD['admin']; but it is suggested to use the auth class for consistency reasons nevertheless.
public
$auth_credencials_found
= false (line 466)
Flag, which is set to true if the system encountered any new login credencials during startup. If this is true, but no user is authenticated, login did fail.
The variable is to be considered read-only.
midcom_services_auth_sessionmgr
$sessionmgr
= null (line 313)
This is a reference to the login session management system.
midcom_core_user
$user
= null (line 293)
The currently authenticated user or null in case of anonymous access.
It is to be considered read-only.
midcom_services_auth_backend
$_auth_backend
= null (line 448)
A reference to the authentication backend we should use by default.
midcom_services_auth_frontend
$_auth_frontend
= null (line 456)
A reference to the authentication frontend we should use by default.
int
$_component_sudo
= 0 (line 440)
This flag indicates if sudo mode is active during execution. This will only be the case if the sudo system actually grants this privileges, and only until components release the rights again. This does override the full access control system at this time and essentially give you full admin privileges (though this might change in the future).
Note, that this is no bool but an int, otherwise it would be impossible to trace nested sudo invocations, which are quite possible with multiple components calling each others callback. A value of 0 indicates that sudo is inactive. A value greater then zero indicates sudo mode is active, with the count being equal to the depth of the sudo callers.
It is thus still safely possible to evaluate this member in a boolean context to check for an enabled sudo mode.
array
$_default_magic_class_privileges
= array() (line 363)
This listing contains all magic privileges assigned to the existing classes. It is a multi-level array, example entry:
'class_name' => Array
(
'EVERYONE' => Array(),
'ANONYMOUS' => Array(),
'USERS' => Array
(
'midcom:create' => MIDCOM_PRIVILEGE_ALLOW,
'midcom:update' => MIDCOM_PRIVILEGE_ALLOW
),
)
array
$_default_privileges
= array() (line 325)
Internal listing of all default privileges currently registered in the system. This is a privilege name/value map.
Array
$_group_cache
= array() (line 381)
Internal cache of all loaded groups, indexed by their identifiers.
bool
$_internal_sudo
= false (line 419)
This is an internal flag used to override all regular permission checks with a sort-of
read-only privilege set. While internal_sudo is enabled, the system automatically grants all privileges except midgard:create, midgard:update, midgard:delete and midgard:privileges, which will always be denied. These checks go after the basic checks for not authenticated users or admin level users.
Danger, Will Robinson: You MUST NEVER touch this flag unless you are working with the authentication core itself and now very much exactly what you are doing. Misusage of this flag can result in serious secruity risks.
All parts of the core may change this variable (as for example midcom_core_group_virtual does during vgroup member loading).
array
$_owner_default_privileges
= array() (line 338)
Internal listing of all default owner privileges currently registered in the system.
All privileges not set in this list will be inherited. This is a privilege name/value map.
Array
$_privileges_cache
= array() (line 400)
Internal cache of the effective privileges of users on content objects, this is an accociative array using a combination of the user identifier and the objects' guid as index. The privileges for the anonymous user use the magic EVERYONE as user identifier.
Array
$_user_cache
= array() (line 389)
Internal cache of all loaded users, indexed by their identifiers.
array
$_vgroups
= null (line 373)
Internal listing of all known virtual groups, populated from the MidCOM Virtual Groups registry. It indexes virtual group identifiers suitable for get_group with their clear-text names.
Simple constructor, calls base class and initializes the data members where applicable.
The real initialization work is done in initialize.
This is called by $_MIDCOM->generate_error(MIDCOM_ERRFORBIDDEN, ...) if and only if the headers have not yet been sent. It will display the error message and appends the login form below it.
The function will clear any existing output buffer, and the sent page will have the 403 - Forbidden HTTP Status. The login will relocate to the same URL, so it should be mostly transparent.
The login message shown depends on the current state:
If the style element midcom_services_auth_access_denied is defined, it will be shown instead of the default error page. The following variables will be available in the local scope:
$title contains the localized title of the page, based on the 'access denied' string ID of the main MidCOM L10n DB. $message will contain the notification what went wrong and $login_warning will notify the user of a failed login. The latter will either be empty or enclosed in a paragraph with the CSS ID 'login_warning'.
See https://www.midgard-project.org/midcom-permalink-c5e99db3cfbb779f1108eff19d262a7c for further information about how to style these elements.
Checks wether a user has a certain privilege on the given content object.
Works on the currently authenticated user by default, but can take another user as an optional argument.
Checks, wether the given user have the privilege assigned to him in general.
Be aware, that this does not take any permissions overriden by content objects into account. Whenever possible, you should user the can_do() variant of this call therefore. can_user_do is only of interest in cases where you do not have any content object available, for example when creating root topics.
Delete a registered virtual group in the system. This requires the privilege midcom:vgroup_delete assigned to the user (there is no content object checked).
This is a limited version of logout: It will just drop the current login session, but keep the request running. This means, that the current request will stay authenticated, but any subsequent requests not.
Note, that this call will also drop any information in the PHP Session (if exists). This will leave the request in a clean state after calling this function.
Returns a listing of all known(!) virtual groups.
Factory Method: Resolves any assignee identifier known by the system into an appropriate user/group object.
You must adhere the reference that is returned, otherwise the internal caching and runtime state strategy will fail.
Returns the system-wide basic privilege set.
Returns a midcom_core_group instance. Valid arguments are either a valid group identiefier (group:... or vgroup:...), any valid identifier for the midcom_core_group constructor or a valid object of that type.
You must adhere the reference that is returned, otherwise the internal caching and runtime state strategy will fail.
This is a wrapper for get_group, which allows Midgard Group retrieval by its name.
If the group name is unknown, false is returned.
In the case that more then one group matches the given name, the first one is returned. Note, that this should not happen as midgard group names should be unique according to the specs.
Returns the system-wide basic owner privilege set.
Returns a full listing of all currently known privileges for a certain object/user combination.
The information is cached per object-guid during runtime, so that repeated checks to the same object do not cause repeateing checks. Be aware that this means, that new privileges set are not guranteed to take effect until the next request.
Factory Method: Loads a user from the database and returns an object instance.
You must adhere the reference that is returned, otherwise the internal caching and runtime state strategy will fail.
This is a wrapper for get_user, which allows user retrieval by its name.
If the username is unknown, false is returned.
Initialize the service:
Check, wether a user is member of a given group. By default, the query is run against the currently authenticated user.
Returns true if there is an authenticated user, false otherwise.
This call tells the backend to clear any authentication state, then relocates the user to the sites' root URL, so that a new, unauthenticated request is started there.
If there was no user authenticated, the relocate is done nevertheless.
It is optionally possible to override the destination set.
This is a simple helper function which validates whether a given privilege exists by its name. Essentially this checks if a corresponding default privilege has been registered in the system.
Merges a new set of default privileges into the current set.
Existing keys will be silently overwritten.
This is usually only called by the framework startup and the component loader.
If only a single default value is set (type integer), then this value is taken for the default and the owner privilege is unset (meaning INHERIT). If two values (type array of integers) is set, the first privilege value is used for default, the second one for the owner privilege set.
Register a virtual group in the system. This requires the privilege midcom:vgroup_register assigned to the user (there is no content object checked).
The member listing is retrieved by the callback midcom_baseclasses_components_interface::_on_retrieve_vgroup_members().
Request superuser privileges for the domain passed.
STUB IMPLEMENTATION ONLY, WILL ALWAYS GRANT SUDO.
You have to call midcom_services_auth::drop_sudo() as soon as you no longer need the elevated privileges, which will reset the authentication data to the initial credencials.
Validates that we currently have admin level privileges, which can either come from the current user, or from the sudo service.
If the check is successful, the function returns silently.
Validates that the current user has the given privilege granted on the content object passed to the function.
If this is not the case, an Access Denied error is genereted, the message defaulting to the string 'access denied: privilege %s not granted' of the MidCOM main L10n table.
The check is always done against the currently authenticated user. If the check is successful, the function returns silently.
Validates that the current user is a member of the given group.
If this is not the case, an Access Denied error is genereted, the message defaulting to the string 'access denied: user is not member of the group %s' of the MidCOM main L10n table.
The check is always done against the currently authenticated user. If the check is successful, the function returns silently.
Validates, wether the given user have the privilege assigned to him in general.
Be aware, that this does not take any permissions overriden by content objects into account. Whenever possible, you should user the can_do() variant of this call therefore. can_user_do is only of interest in cases where you do not have any content object available, for example when creating root topics.
If this is not the case, an Access Denied error is genereted, the message defaulting to the string 'access denied: privilege %s not granted' of the MidCOM main L10n table.
The check is always done against the currently authenticated user. If the check is successful, the function returns silently.
Validates that there is an authenticated user.
If this is not the case, the regular login page is shown automatically, see show_login_page() for details..
If the check is successful, the function returns silently.
This function should be used to render the main login form. This does only include the form, no heading or whatsoever.
It is recommended to call this function only as long as the headers are not yet sent (which is usually given thanks to MidCOMs output buffering).
What gets rendered depends on the authentication frontend, but will usually be some kind of form. The output from the frontend is surrounded by a div tag whose CSS ID is set to 'midcom_login_form'.
See https://www.midgard-project.org/midcom-permalink-c5e99db3cfbb779f1108eff19d262a7c for further information about how to style these elements.
This will show a complete login page unconditionally and exit afterwards.
If the current style has an element called midcom_services_auth_login_page it will be shown instead. The local scope will contain the two variables $title and $login_warning. $title is the localized string 'login' from the main MidCOM L10n DB, login_warning is empty unless there was a failed authentication attempt, in which case it will have a localized warning message enclosed in a paragraph with the ID 'login_warning'.
See https://www.midgard-project.org/midcom-permalink-c5e99db3cfbb779f1108eff19d262a7c for further information about how to style these elements.
This internal helper checks if a privilege is available during internal sudo mode, as outlined in the corresponding variable.
Internal startup helper, checks the currently running authentication backend for a running login session.
Internal startup helper, checks if the current authentication fronted has new credencials ready. If yes, it processes the login accordingly.
Internal startup helper, synchronizes the authenticated user with the Midgard Authentication for startup. This will be overridden by MidCOM Auth, but is there for compatibility reasons.
This helper function loads and prepares the list of class magic privileges for usage. It will assign them to the $_*_default_class_privileges members.
Internal startup helper, loads all configured authentication drivers.
This internal helper will initialize the default priviliges array with all core privileges currently defined.
Internal helper, synchronizes the main service class with the authentication state of the authentication backend.
Inherited From midcom_baseclasses_core_object
midcom_baseclasses_core_object::midcom_baseclasses_core_object()
Documentation generated on Tue, 15 Aug 2006 12:20:55 +0300 by phpDocumentor 1.3.0RC3