Versions Compared

Key

  • This line was added.
  • This line was removed.
  • Formatting was changed.
Comment: Migrated to Confluence 5.3

...

A new design is being proposed, but it is not finalized. Anyone who is interested is welcome to comment, critique, and add suggestions.

Security-Aware Artifacts

Introduction

The current OFBiz security implementation uses indirect security control - where permission services are used to control access to OFBiz artifacts. Permission services are small scripts that evaluate user permissions, determine if a user is related to a piece in data in some way, and other tasks. In the end, the script returns an access-granted or access-denied result (hasPermission) that is used to control access to the artifact(s) managed by the script.

...

A permission service can be extended by assigning a Service Event Condition Action (SECA) to it. If the primary permission service returns hasPermission=false, the SECA is triggered and an additional permission service is run. The result of the second permission service controls access to the artifact(s).

Disadvantages

Since the current OFBiz security implementation uses indirect security control, there is no direct connection of permission logic to the artifact that the logic is trying to control access to. Access to a particular artifact could be (and often is) controlled by a handful of different scripts. While this may appear to be an advantage (because it's flexible), it becomes a problem when something about the artifact changes that requires a change in access control. Permission scripts that control the artifact must be tracked down and updated.

Permission checking is optional. Through oversight or laziness artifacts can be left unprotected - opening up security holes or attack vectors.

The coarse grained permission logic fosters an "all or nothing" design mentality. Smaller artifacts - like menu items and data entry fields - can be controlled individually by permissions, but it requires cumbersome conditional code. Many times the developer will just give the user access to all of these smaller artifacts - regardless of whether or not the user has permission to use them.

...

Any time a new artifact is created that needs access control, a new script needs to be written to control it.

A Different Approach

What if we took the job of artifact access control away from the permission services and gave it to the artifacts themselves? What if every artifact was security-aware?

Instead of an artifact's access being controlled in a handful of places, it is controlled in one place - within the artifact itself. There is no complicated intermediary code that must consider all possible interactions a user might have with a set of artifacts. Since all artifacts are security-aware, fine grained control is possible. There is no need for cumbersome conditional logic to control an artifact - the artifact controls itself.

Design Overview

There are three key players in the Security-Aware Artifact design:

...

Default behaviors for types of artifacts will be well documented, so the developer and OFBiz administrator will know exactly how permissions will affect each artifact.

Design Details

If each artifact has its own set of permissions, there could be potentially thousands of permissions - an administration nightmare! It would be helpful to have artifact permissions organized in a hierarchy - so that permissions defined higher up in the hierarchy are inherited by artifacts lower in the hierarchy. The hierarchy will reduce the number of permissions needed.

Note

The following hierarchy has been called a "static hierarchy" and it is not finalized. There is an alternative to it called "dynamic hierarchy" - see below.

The components in OFBiz are organized in a hierarchy, and each component typically contains entities, services, and screen widgets. We can build on that structure to set up a hierarchy of security-aware artifacts:

...

  • accounting
    • entity
      • Budget
      • BudgetAttribute
      • ...
      • PartyRate
    • service
      • createPartyAcctgPreference
      • updatePartyAcctgPreference
      • ...
      • payflowCCRefund
    • screen
      • FindAgreement
      • EditAgreement
      • ...
      • ViewGatewayResponse

An execution path could be considered a hierarchy. We can build on that structure to set up a hierarchy of security-aware artifacts:

  • OFBiz
    • Component
      • WebApp
        • ControllerRequest
          • ControllerRequestEvent
            • Service
              • Entity
                • EntityField
        • ControllerView
          • WidgetScreen
            • Service
              • Entity
                • EntityField
            • FtlFile
              • WidgetScreen
            • WidgetForm
              • WidgetFormField
    • ...

Permissions assigned at the OFBiz/applicationscomponent/accounting webapp level are inherited by all artifacts below that level. Permissions assigned at lower levels replace any inherited permissions. Permission inheritance is managed by the authorization manager.

In the design overview, it was mentioned that the security-aware artifact provides its identifier to the authorization manager when requesting permissions for itself. That identifier must be unique in order for the system to work. We can leverage the artifact hierarchy to construct a unique artifact identifier. The Budget entity's identifier would be OFBiz/applications/accounting/entity/Budget. The FindAgreement screen's identifier would be OFBiz/applications/accounting/screen/FindAgreement. The authorization manager should handle the identifier in a case-insensitive way.

The "OFBiz" portion of the identifier is considered the root of the hierarchy. The "applications" and "framework" portions of the identifiers could be left out of the design.

The permission list returned by the authorization The permission list returned by the authorization manager is a set of flags - implemented as key/value pairs. The flag key indicates the type of permission - view, create, update, delete, etc. Valid flag values are "true" or "false." A value of "true" indicates access-granted, and a value of "false" or the absence of a key/value pair indicates access-denied. The permission list could consist of the standard view, create, update, and delete permissions. Artifacts are allowed to define additional permissions. For example, a web application or service artifact might need only one permission - access.

...

  1. The entity engine passes an unfiltered list of entities to the service, the service picks matching records from the list and returns them. The advantage to this approach is we can use existing code to implement it. The disadvantage is it is inefficient.
  2. The service returns an EntityCondition that represents the constraint, and the entity engine merges that EntityCondition with any condition it already has. The advantage to this approach is the list of entities is filtered by the database. The disadvantage is there is no way to return an EntityCondition in mini-language. Mini-language would have to be extended, or the EntityCondition would have to be constructed in a Beanshell or Groovy script, or the filters would have to be written in Java.

The Authorization Manager

The Authorization Manager (AM) is a piece of software responsible for managing users and and their permissions.

...

An artifact provides a UserLogin GenericValue and an artifact identifier to the AM for a permissions request. The AM uses the UserLogin to look up the user's permissions in external storage. This would include all groups the user is a member of, and the groups that those groups are members of, etc. The AM compiles a list of the user's permissions, searches the list for the artifact's identifier, and then returns whatever permissions are found.

Although design implementation is beyond the scope of this document, for the For the sake of illustration let's say the AM builds an internal tree structure from all of the user's permissions. The tree structure would look just like the artifact hierarchy - but it only includes artifact identifiers the user has permissions for. Using the requesting artifact's identifier (and working from left to right), the AM "walks the tree" - looking for permission flags and permission services. While the AM walks the tree, it keeps a copy of the last permissions found. Every time new permissions are found, they replace the previous copy (that is how permission inheritance is achieved). Eventually, the AM will encounter the end of the tree or the end of the artifact identifier. When that happens, the AM returns the last permissions found.

The OFBiz User Group

Sometimes it is desirable to assign some basic permissions to ALL OFBiz users. Mailing list comments indicate that some installations meet that need by using a custom CreateUser service, or a SECA that adds the permissions to new users, or some other solution.

...

In this design, that need is fulfilled by having a default (or "out-of-the-box") User Group called OFBiz Users. The Authorization Manager will make all new users a member of that group, and consequently that group will be included in all permission checks. The Authorization Manager will throw an exception any time an attempt is made to delete the "OFBiz Users" user group.

The

...

Not-Logged-In User

The Security-Aware Artifact design introduces a fundamental problem: How do you get a user logged in? The various artifacts needed to log in a user won't operate without a UserLogin GenericValue present (and necessary permissions assigned to that user).

In this design, the problem is solved with a Not-Logged-In user account. That user's account is disabled, and it is assigned only the permissions needed to get a user logged in. Front-end processors (event/request handlers, etc) create the Not-Logged-In UserLogin GenericValue so that artifacts needed for login can be used.

The Super User

It is common to have a way to identify an administrator user, or super user. There are two ways that can be accomplished in this design:

  1. Have an admin permission.
  2. Have an admin User Role.

Although the term "admin" implies a user role, it would be more flexible to make it a permission. A user could be granted the admin permission to any artifact, which would make the user the admin of that artifact and all the artifacts below it. If an "Admin" User Role was desired, the admin permission could be assigned to a User Group called "Admin User Role" - and all the members of that group would become admins.

A user could be made an admin of all of OFBiz by granting the user admin permission to the OFBiz (root) artifact.

Artifacts must treat the admin permission

It is common to have a way to identify an administrator user, or super user. There are two ways that can be accomplished in this design:

  1. Have an admin permission.
  2. Have an admin User Role.

Although the term "admin" implies a user role, it would be more flexible to make it a permission. A user could be granted the admin permission to any artifact, which would make the user the admin of that artifact and all the artifacts below it. If an "Admin" User Role was desired, the admin permission could be assigned to a User Role called "Admin" - and all the members of that role would become admins.

A user could be made an admin of all of OFBiz by granting the user admin permission to the OFBiz (root) artifact.

Artifacts must treat the admin permission as expected - it is the same as having all permissions. If the admin permission appears in a list of permission flags, it takes precedence - all other permission flags and permission services are ignored.

Artifact Reuse

Handling permissions with reused artifacts is greatly simplified with the Security-Aware Artifacts design. The problems associated with the existing intermediary design plus coarse-grained hard-coded permissions are eliminated.

When a screen widget artifact is reused in another component, that artifact's identifier changes . - since its execution path is different. For example, if I reuse the Example component's EditExample screen in my specialpurpose/NewApplication component (by including it in my own screen definition), the reused EditExample screen artifact identifier changes from OFBiz/framework/example/screen/EditExample to OFBiz/specialpurpose/NewApplication/screen/EditExample. Permissions assigned to the latter will not grant access to the former.

The form in the reused EditExample screen will generate an updateExample event which, in turn, invokes the updateExample service. Just like the EditExample screen, the updateExample service artifact's identifier changes - I can give the users of NewApplication permission to access OFBiz/frameworkNewApplication/example/serviceupdateExample/updateExample without giving them access to any other Example component artifacts.

Not all artifact reuse will be this simple. But the more complicated scenarios can be accommodated by finding ways to "graft" the reused artifact into the new component's artifact hierarchy - which will provide a means to assign permissions to the reused artifact.

Proposed Implementation Details

Existing Entities to Use:

Transition

The existing security design and the new Security-Aware Artifact design can coexist. This ability will ease the transition to the new design.

Initially, the OFBiz User Group will have admin permissions assigned to it - one for each component (or group of artifacts). This will effectively disable the security checking in the new design - since the admin permission grants access to everything. The existing hard-coded permission checks will take care of security. As each component is converted over to the new design (hard-coded security checks are removed), the OFBiz User admin permission for that component is removed and the new design takes over security checking for that component (or group of artifacts).

Proposed Implementation Details

Note

DEJ20090528: After thinking about this data model more and discussing it with a couple of others I don't think we should use what I have outlined below and instead we should create a more normalized data model. The main difference would be a few different entities in place of the bloated and denormalized SecurityArtifactAuth entity.

Existing Entities to Use:

UserLogin
UserLoginSecurityGroup
SecurityGroup

New Entities to Add:

SecurityArtifactAuth
-securityArtifactAuthId*
-securityGroupId
-securityArtifactGroupId
-authTypeEnumId (NotSpecified, Allow, Deny, AlwaysAllow (overrides Deny))
-authorizedActionEnumId (View, Create, Updated, Delete, All)
-recordViewEntityName
-recordFilterByDateEnumId (True, False, ByName)
-userMatchFieldName - for record level matching will lookup in recordViewEntityName view-entity for the currently logged in userLoginId or partyId, and if this field is set it will use this field name to match against, otherwise it will look for the userLoginId field or the partyId field in the view-entity
-authServiceName - this service will be called according to the pattern of current auth services and must return true/approved in addition to anything else passing that is populated in this entity

...

NOTE: we may want to move some fields between SecurityArtifactAuth and SecurityArtifactGroupMember as some things could go in either place, but should only go into one and which one they make sense in is a good question (and I've taken a pass here, but we may want to change it)

Possible Entities to Add:

-Could use these to make view-entity configurations on the fly (this may be WAY too technical for most users, so considering this a pretty low priority)

...

DynamicViewEntityJoin
entityAlias
relEntityAlias
keyName
relKeyName
relOptional

Order of Artifact Checking - For WebApp Request

An ExecutionContext object is maintained that has an artifactAccessStack. In the artifactAccessStack as each artifact calls/users another artifact it is pushed onto the stack, and when it finished it pops itself off the stack. With this at any point we can see which artifacts were used to get to where we are. For the higher-level webapp-specific artifacts this will be kept in the request, for the widgets this will be kept in their context, for services it will be in the context, and for entities it will be in a thread-local variable. This is called an ExecutionContext because it may be used for more than just security/authorization purposes, and in fact we should refactor the current code that keeps track of the user and other things in ThreadLocal variables (mostly for the entity engine) so that they are in this ExecutionContext object.

The result of each artifact access control check will include:

  • authTypeEnumId (NotSpecified, Allow, Deny, AlwaysAllow (overrides Deny))
  • inheritSubArtifactAuth (Y, N)

Whenever an artifact is hit the authorization (access control) will be checked and the results of it will be kept in the artifactAccessStack. In order to support the inheritParentArtifactAuth field, as part of the ExecutionContext we will maintain a parentAuthorizationState variable that keeps track of the authTypeEnumId of the last artifact that had inheritParentArtifactAuth=Y. If an artifact called by the parent artifact passed but does not have inheritParentArtifactAuth=Y then the parentAuthorizationState variable will not be changed.

Note

David, I don't think the ExecutionContext object should be concerned with security or the permissions hierarchy - that is the Authorization Manager's responsibility. All the ExecutionContext needs to provide is an artifact ID that the artifact can pass on to the Authorization Manager. -Adrian

The reason The reason for this is that depending on the parentAuthorizationState the permission still needs to be checked but the results are interpreted differently:

  • if parentAuthorizationState=NotSpecified:
    • authTypeEnumId interpreted as-is
  • if parentAuthorizationState=Allow:
    • authTypeEnumId=NotSpecified: auth passed
    • authTypeEnumId=Allow: auth passed
    • authTypeEnumId=Deny: auth failed
    • authTypeEnumId=AlwaysAllow: auth passed
  • if parentAuthorizationState=AlwaysAllow:
    • authTypeEnumId=NotSpecified: auth passed
    • authTypeEnumId=Allow: auth passed
    • authTypeEnumId=Deny: auth passed
    • authTypeEnumId=AlwaysAllow: auth passed

Note that parentAuthorizationState will never be set to "Deny" because before that would happen the auth would have failed and an error sent back up the stack.

Note

The following hierarchy has been called a "dynamic hierarchy" and it is not finalized. There is an alternative to it called "static hierarchy" - mentioned above.

Example Chain:

  • Component
    • WebApp
      • ControllerRequest
        • ControllerRequestEvent
          • Service
            • Entity
              • EntityField
      • ControllerView
        • WidgetScreen
          • Service
            • Entity
              • EntityField
          • FtlFile
            • WidgetScreen
          • WidgetForm
            • WidgetFormField

Authorization Manager Implementation

A new Java interface will be created, called AuthorizationManager. The existing Security abstract class will implement the AuthorizationManager interface. During the transition, Security will play a dual role - the legacy security implementation and the new implementation.

When the conversion to the new design is complete, the SecurityFactory and other classes will be updated to use the AuthorizationManager interface instead of the Security abstract class. Developers will be able to write their own AuthorizationManager implementations that are backed by third party authorization software (LDAP, Active Directory, etc).

...

and an error sent back up the stack.

Execution Context Implementation

Details can be found here.

Authorization Manager Implementation

The existing security classes will be refactored so the Security abstract class is converted to an interface, and the OFBizSecurity class will implement the interface. The Authorization Manager methods will be added to the Security interface, and the existing methods will be deprecated. This will make the conversion backward-compatible with existing installations. Details

Info

The security classes have been refactored and committed. The Authorization Manager methods will be a separate interface, and the Security interface will extend it.

Access Control Scenarios

1. User X can use Artifact Y for anything that artifact supports and on any data (where "artifact" is a screen, web page, part of a screen or page, service, general logic, etc). Details

...