• Views
  • Iteration Report
  • My Iteration Report
  •  
OMERO.server
  • Login
  • Help/Guide
  • About Trac
  • Preferences
  • Wiki
  • Timeline
  • Roadmap
  • Browse Source
  • View Tickets
  • Search

Context Navigation

  • Start Page
  • Index
  • History
  • Last Change

Omero Security System

Table of Contents

  1. Concepts
  2. Participants
    1. Top-level and Build
      1. JBoss-only
      2. OmeroGrid only
    2. Client & Common
      1. JBoss-only
      2. OmeroGrid-only
    3. Server side
  3. End-to-end
    1. Build system
    2. Client-side
    3. Application server
    4. Server code
    5. Interceptors
    6. Services
    7. Hibernate
    8. Security System
  4. Logging in (client-side)
  5. Logging in (server-side)

The Omero security system is intended to be as transparent as possible while permitting users to configure the visibility of their data. For the user, this means that with no special actions, data and metadata created will be readable by both members of the same group and by other users, but will be writable by no one, comparable to a umask of 755 on Unix. For the developer, transparency means that there is little or no code that must be written to prevent security violations, and simple mechanisms for allowing restricted operations when the time comes.

Other links which may be of use:

  • AccessControl
  • AdminInterface
  • SecurityUseCases
  • SecurityRoles
  • PermissionsAndUmasks

Concepts

Several concepts and/or components from our and other code bases play a staring role in Omero security.

Hibernate Listeners and Events (docs)
listeners and events are the two extension points provided by Hibernate for responding to and influencing internal actions. Essentially any method on the org.hibernate.Session interface has a corresponding event, and almost the same is true for the interceptor. Additionally interceptors can change the state of the objects before INSERT and UPDATE, and after SELECT.
Hibernate Filters (docs)
filters are a mechanism for injecting SQL clauses into the SELECT statements generated by Hibernate. Similar to listeners and events for write actions, filters allow us to extend Hibernate functionality with our own logic.
Handler/interceptor
as outlined in OmeroAop, Omero makes extensive use of method interceptors to relieve the developer of some coding burden. Transactions, session management, and, naturally, security are handled largely by our interceptors (or "handlers").
Events
Every write action produces an Event in the DB. This DB contains several EventLogs which specify exactly what was created or altered during that specific event.

Participants

Now, with the concepts cleared up, let's take a look at all of the concrete source artifacts ("participants") which are important for security.

Top-level and Build

etc/local.properties
contains login and connection information for the DB and Omero.
local.properties.example
contains the default root password. This can be overriden with your own etc/local.properties file.
hibernate.properties
contains default connection information for the database, this includes the user name and if necessary the user password. These values can be overridden in local.properties.
omero.properties
contains a default user group, event type, and connection information for logging in from the client side, if no Login or Server is specified to ServiceFactory. These values can be overridden in local.properties.
mapping.vm
specifies the default permissions that all objects will have after construction, as well as attaches the security filter to all classes and collections.
data.vm
used by DSLTask to generate data.sql which is used to bootstrap the db security system (root et al)
common/build.xml
contains an ant target (adduser) which will create a user and empty password from the commandline. This target can also be called from the top-level (java omero adduser).

JBoss-only

etc/jndi.properties
defines the JNDI implementation to use. In the case of JBoss, it uses an InitialContext? which automatically passes the security Principal on service lookup. These values can be overridden in local.properties.

OmeroGrid only

Client & Common

ome/client/spring.xml
uses the information in /etc/local.properties to create a Login object. If no Login, Server, or Properties is provided to the ServiceFactory constructor, the empty properties defined in ome/client/internal.xml is used.
IAdmin.java
main interface for administering accounts and privileges. See AdminInterface for more.
ITypes.java
only related to security by necessity. The security system disallows the creation of certain "System-Types". Enumerations are one of these. ITypes, however, provides a createEnumeration method with general access.
GraphHolder.java
all model objects (implementations of IObject have a never-null GraphHolder instance available. This graph holder is responsible for various Omero & Hibernate internal processes. One of these is the exchange of Tokens. For the server, the existance of a special token within the GraphHolder? grants certain privileges to that IObject. This logic is encapsulated within the SecuritySystem.
Details.java
contains all the fields necessary to perform AccessControl, such as owner, group, and permissions.
Permissions
representation of rights and roles. For more information, see PermissionsAndUmasks.
Token.java
an extremely simple class ("public class Token {}") which is only significant when it is equivalent ("==") to a privileged Token stored within the SecuritySystem.
IEnum.java
the only non-access control related types which are considered "System-Types" are enumerations. IEnum is a marker interface for all enumerations and creation of IEnum implementations can only be performed through ITypes.
SecurityViolation.java
the exception thrown by the SecuritySystem at the first hint of misdoings.
Principal.java
an Omero-speciific implementation of the java.security.Principal interface. Carries in addition to the typical name field, information about the user group, the event type, and the session umasks.
meta.ome.xml

JBoss-only

ServiceFactory.java
Login.java
Server.java

OmeroGrid-only

Server side

AdminImpl.java
CurrentDetails.java
SecureAction.java
SecuritySystem.java
BasicSecuritySystem.java
ACLEventListener.java
EventDiffHolder.java
EventHandler.java
MergeEventListener.java
OmeroInterceptor.java
SessionHandler.java
SecurityFilter.java
EventLogListener.java
EventListenersFactoryBean.java
LocalAdmin.java
aop.xml
hibernate.xml
security.xml
services.xml
AbstractBean.java
contains the wrap() method used by all concrete session beans to before the necessary context prep for login in EventHandler.
*Bean.java
All the concrete beans are responsible for defining the @javax.ejb.security.RolesAllowed? annotation to support SecurityRoles.
AOPAdapter.java
applies the Spring-defined interceptors (including EventHandler) to the session beans.

End-to-end

Build system

Security starts with the build system and installation. During the generation of the model (by the DSLTask), a sql script is created called "data.sql". After ddl.sql creates the database, data.sql bootstraps the security system by creating the initial (root) experimenter, and event, and then creates the "system" group and the "user" group. It then creates a password table and sets the root password to "ome". (It also creates all of the enumeration values, but that's rather unimportant for security).

Note: the password table is not mapped into Hibernate, and is only accessible via the AdminInterface.

Client-side

To begin the runtime security process, a user logs in by providing a Login and/or a Server instance to ServiceFactory. These types are immutable and their values remain constant for the lifetime of the ServiceFactory. The user can also set the umask property on ServiceFactory_. This value is mutable and can be set at anytime.

The values are converted to java.util.Properties which are merged with the properties from the *.properties files from /etc to create the client OmeroContext (also known as the "application context"). The context contains a Principal and user credentials (password, etc.) which are associated with the thread before each method execution in a specialized TargetSource. Finally, these objects are serialized to the application server along with the method arguments.

Application server

The application server first performs one query (most likely SQL) to check that the credentials match those for the given user name. A second query is executed to retrieve all roles/groups for the given user. If the roles returned are allowed to invoke the desired method, invocation continues with the queried user and roles stored in the InvocationContext.

Server code

Execution then passes to Omero code, specifically to the interceptors and lifecycle methods defined on our session beans. This intercepting code checks the passed Principal for Omero-specific information. If this information is available, it is passed into the SecuritySystem through the login method. Finally, execution is returned to the actual bean which can either delegate to Omero services or perform logic themselves.

Interceptors

All calls to the delegates (and in the future all calls on the session beans) are also caught intercepted by Spring-configured interceptors. These guarantee that the system is always in a valid and secure state. In stack order they are: * the service handler, which handles logging and checks all arguments against ServiceInterface annotations; * the proxy handler, which after execution, removes all uninitialized Hibernate objects to prevent exceptions (special logic allows this to happen See unloaded objects); * the transaction handler, which binds a transaction to the thread, * the session handler, which uses the now preared transaction to initialize either a new or a cached (in the case of stateful session beans) session and also bind it to the thread; * and finally, the event handler, which performs what one might actually consider login. It instatiates Exerimenter, ExperimenterGroup, and Event objects from Hibernate and gives them a special Token so that they can authenticate themselves later to the SecuritySystem and turns session read security on for the entirety of execution below its frame.

Services

Finally execution has reached the Omero services and can begin to perform logic. Because of these layers, almost no special logic (other than eviction and not calling write methods from within read methods. see #223) needs to be considered. There are, however, a few special cases.

IQuery (within the application server), for example will always return a graph of active Hibernate objects. Changes to them will be persisted to the db on flush.

IUpdate, on the other hand, does contain some logic for easing persistence, though this will eventually be ported to the Hibernate event system. This includes pre-saving the newly created event and the work of UpdateFilter like reloading objects unloaded by the proxy handler (above).

Finally, IAdmin is special in that it and it alone access the non-Hibernate password data store and even access application server APIs (like JMX) in order to make authentication and authorization function properly.

Hibernate

Once execution has left this service layer, it enters the world of Hibernate ORM. Here we cannot actively change functionality but only provide callbacks like the OmeroInterceptor and EventListeners. The OmeroInterceptor instance registered with the Hibernate SessionFactory (via Spring) is allowed for calling back to the oft mentioned SecuritySystem to determine what objects can be saved and which deleted. It also properly sets the, for a user mostly unimportant Details object. The EventListeners are more comprehensive than the OmeroInterceptor and can influence almost every phase of the Hibernate lifecycle, specifically every method on the Session interface. (Sadly, these are under-documented).

The event listeners which implement AbstractSaveEventListener (i.e. MergeEventListener, SaveOrUpdateEventListener, ... ) are responsible for reloading unloaded objects (and will hopefully take this functionality fully from IUpdate) and provide special handling for enums and other system types. There are also event listeners which are the equivalent of DB triggers (pre-update, post-delete, etc.) and these are used for generating our audit log.

So much for write activities. Select queries are, as mentioned above, secured through the use of Hibernate filters which add join and where clauses dynamically to queries. For example an HQL query of the form:

   select i from Image i

would be filtered so that the current user doesn't receive references to any objects with reduced visibility:

   select i from Image i where ( current_user = :root OR i.permissions = :readable )

The actual clauses added are much more complex and are added for each joined entity type (i.e. table) which apears in a query.

   select i from Image i join i.defaultPixels p

would contain the "( current_user = :root ...)" clause twice.

Currently, subqueries are an issue in that the clauses don't get added to them. This may cause consternation for some particular queries.

Security System

All of this is supported by an implementation of the SecuritySystem interface which encapsulates all logic regarding security. It also hides as much as it can, and if not specifically needed should be ignored. However, before one attempts to manually check security, by all means use the security system, and for that, it may need to be acquired from the server-side OmeroContext. Currently, there is no client-side security system. See ticket:234.

The SecuritySystem and its currently only implementation BasicSecuritySystem? are somewhat inert and expect well-defined and trusted (see ticket:235) methods to invoke callbacks during the proper Hibernate phase.

Logging in (client-side)

When using the client library and the ServiceFactory, logging in is trivial. One need only set several System properties or place them in an omero.properties file somewhere on the classpath. MoreToCome?. Internally, Spring takes the System properties and creates an ome.system.Principal instance. This is then passed to the server on each invocation of a proxy obtained from JNDI.

Logging in (server-side)

Much of this infrastructure is not available to server-side code (no ome/client/spring.xml, no ServiceFactory, etc.). As such, the Principal needs to be manually created iand provided to the server-side SecuritySystem.java.

Basically it amounts to this:

  Principal p = new Principal( omeroUserName, omeroGroupName, omeroEventTypeValue );
  securitySystem.login( p );

This must be run otherwise the EventHandler will throw a security exception. Note: the code above is being run in a secure context (i.e. you are root.) Pease be careful.

For examples see: * source:trunk/components/client/resources/ome/client/spring.xml for how a Principal instance is created. * source:trunk/components/server/src/ome/services/util/OmeroAroundInvoke.java] for how the Principal instance is used.

Download in other formats:

  • Plain Text

Trac Powered

Powered by Trac 0.11
By Edgewall Software.

Visit the Trac open source project at
http://trac.edgewall.org/