Skip to main content

Notice: this Wiki will be going read only early in 2024 and edits will no longer be possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "Scout/HowTo/4.0/Create Permissions"

< Scout‎ | HowTo‎ | 4.0
(Created page with "== Introduction == Permissions are needed to control: # Who is allowed to do what action # Who is allowed to see which records == Permission to do certain Actions == To enf...")
 
(Replaced content with "The Scout documentation has been moved to https://eclipsescout.github.io/.")
 
Line 1: Line 1:
== Introduction ==
+
The Scout documentation has been moved to https://eclipsescout.github.io/.
Permissions are needed to control:
+
 
+
# Who is allowed to do what action
+
# Who is allowed to see which records
+
 
+
== Permission to do certain Actions ==
+
 
+
To enforce that not everybody can do everything, permission are created. A permission has a name, e.g. <code>ReadCompanyPermission</code>. The permissions are grouped into roles, e.g. Standard role or Administrator role. Each user is assigned to one or more roles.
+
 
+
===Create a Permission===
+
 
+
To create a permission in Scout, use the menu "New Permission..." on folder "Permissions" below folder shared. This creates a class that extends <code>BasicPermission</code> or <code>BasicHierarchyPermission</code>.<br>
+
When creating e.g. a new Scout Form or Process Service using the Scout SDK, corresponding permissions are created by default as well.
+
 
+
===Use Permissions (Client Side)===
+
 
+
On a table page, the following methods checks whether the logged in user has the necessary permission and depending on it sets the visibility of the table page to true or false.
+
<source lang="java">
+
@Override
+
public void execInitPage() throws ProcessingException{
+
  setVisibleGranted(ACCESS.getLevel(new ReadCompanyPermission()) > BasicHierarchyPermission.LEVEL_NONE);
+
}
+
</source>
+
 
+
===Use Permissions (Server Side)===
+
 
+
When creating a process service with Scout SDK, permission classes are created with it. For the methods <code>prepareCreate</code>, <code>create</code>, <code>load</code> and <code>store</code>, the following code is added (on the example of a <code>CompanyProcessService</code>):
+
<source lang="java">
+
public CompanyFormData prepareCreate(CompanyFormData  formData) throws ProcessingException{
+
  if(!ACCESS.check(new CreateCompanyPermission())){
+
    throw new VetoException(TEXTS.get("AuthorizationFailed"));
+
  }
+
  // TODO business logic here
+
  return formData;
+
}
+
 
+
public CompanyFormData create(CompanyFormData  formData) throws ProcessingException{
+
  if(!ACCESS.check(new CreateCompanyPermission())){
+
    throw new VetoException(TEXTS.get("AuthorizationFailed"));
+
  }
+
  // TODO business logic here
+
  return formData;
+
}
+
 
+
public CompanyFormData load(CompanyFormData  formData) throws ProcessingException{
+
  if(!ACCESS.check(new ReadCompanyPermission())){
+
    throw new VetoException(TEXTS.get("AuthorizationFailed"));
+
  }
+
  // TODO business logic here
+
  return formData;
+
}
+
 
+
public CompanyFormData store(CompanyFormData  formData) throws ProcessingException{
+
  if(!ACCESS.check(new UpdateCompanyPermission())){
+
    throw new VetoException(TEXTS.get("AuthorizationFailed"));
+
  }
+
  // TODO business logic here
+
  return formData;
+
}
+
</source>
+
 
+
The <code>ACCESS.check(Permission p)</code> checks, whether the logged in user has the permission p. If not, an exception is thrown, which results in a message box popping up saying that the user is not authorized to do this action.
+
 
+
 
+
== Assigning Permissions to Users ==
+
The permissons are assigned to a user in the <code>AccessControlService</code> [http://wiki.eclipse.org/Image:Scout.3.8.howto.permissions.01.png]. This service is typically created and registered by the Scout SDK when creating a new Scout Project.
+
 
+
===Example: Load permissions from a database===
+
Let us say you have a role based security concept with the 2 database tables USER_ROLE AND ROLE_PERMISSION. In the table USER_ROLE, there you link the user to several roles whereas the table ROLE_PERMISSION holds all the permissions that belong to those roles. Then you can select the available permission levels for the current user and then convert it to a permission list using <code>AccessControlUtility.createPermissions(permissionData)</code>.
+
 
+
Furthermore you have to add the builtin permission <code>RemoteServiceAccessPermission</code> to the collection of permissions to allow the user to call backend services. The two arguments allow you to constrain the kind of services this user is allowed to call. You can use the wilcard character * to specify a matching pattern.
+
 
+
Your <code>AccessControlService</code> could look like this:
+
<source lang="java">
+
@Override
+
protected Permissions execLoadPermissions() {
+
  try {
+
    Object[][] permissionData = SQL.select(
+
            "SELECT  P.PERMISSION_NAME, " +
+
            "        MAX(P.PERMISSION_LEVEL) " +
+
            "FROM    USER_ROLE R, ROLE_PERMISSION P " +
+
            "WHERE    R.ROLE_UID=P.ROLE_UID " +
+
            "AND      P.PERMISSION_LEVEL>0 " +
+
            "AND      R.USER_NR = :userNr " +
+
            "GROUP BY P.PERMISSION_NAME"
+
    );
+
 
+
    Permissions p = AccessControlUtility.createPermissions(permissionData);
+
    p.add(new RemoteServiceAccessPermission("*.shared.*", "*"));
+
    return p;
+
  }
+
  catch (ProcessingException t) {
+
    //TODO: add logging
+
    return null;
+
  }
+
}
+
</source>
+
 
+
If your permissions are stored elsewhere, no problem. Just prepare the permissionData matrix as required: First column with the name of the class, second column with a numerical "level".
+
 
+
{{Note|Full Access|There exists the permission <code>AllPermission</code> which implies all other permissions. For full-access, please add this permission to the collection.}}
+
 
+
==== Fill database tables ROLE_PERMISSION AND USER_ROLE ====
+
Put an entry into the table '''ROLE_PERMISSION''' to represent the permission. Also, create a role entry to associate this permission with a role the user belongs to. Please note, that in the example above, the '''PERMISSION_NAME''' would be <code>x.y.shared.security.ReadCompanyPermission</code>. The level is not of interest for this kind of permission, so simply put <code>PERMISSION_LEVEL=100</code>. More on the topic 'level' will follow further down.
+
 
+
 
+
==How Permissions are Discovered==
+
 
+
Permission are discovered by IPermissionService. The default implementation looks for permission classes in all bundles installed in the OSGi environemnt. Thereby, the following criteria must be satisfied:
+
* The class must be of the type '''java.security.Permission'''
+
* The type must be a public concrete class, meaning not an interface nor an abstract class
+
* Class must have the token '''Permission''' in its name
+
* The class must be located in a package with '''.security.''' in its package name
+
 
+
{{Note|Change Permission Discovery|This behavior can be overwritten by writing an own implementation of <code>IPermissionService</code> and giving the own implementation a higher priority.}}
+
 
+
 
+
==Fine-grained Access Control==
+
 
+
Further, it is possible to use fine-grained access permissions. This is in contrast to the BasicPermission mentioned above, which simply handles 'go' or 'no-go' situations.
+
 
+
However, fine-grained access permissions must be of the type BasicHierarchyPermission. Furthermore, '''PERMISSION_LEVEL''' in the database table '''ROLE_PERMISSION''' comes into play, meaning that in there, you specify the access level on its behalf the user can access protected resources.
+
 
+
The concept is based on various levels in the range from 0 up to 100. Thereby, 0 means no-access, whereas 100 mean full-access. Basically, if the permission of the user (loaded from database) has a level higher or equals than/to the level requested, access is granted.
+
 
+
In '''BasicHierarchyPermission''', the following levels are defined:
+
 
+
  LEVEL_NONE = 0
+
  LEVEL_ALL = 100
+
  LEVEL_UNDEFINED = -1
+
 
+
Again, let us elaborate a tiny example:
+
The requirement would be that users should only access companies which they really belong to. For that purpose, we introduce a new access level '''LEVEL_OWN'''=10.
+
 
+
The permission <code>ReadCompanyPermission</code> would be changed as follows:
+
 
+
<source lang="java">
+
public class ReadCompanyPermission extends BasicHierarchyPermission {
+
  public static final int LEVEL_OWN = 10;
+
 
+
  private Long m_companyId;
+
 
+
  public ReadCompanyPermission(Long companyId) {
+
    super("ReadCompany" + "." + companyId, LEVEL_UNDEFINED);
+
    m_companyId = companyId;
+
  }
+
 
+
  protected boolean execCheckLevel(int userLevel) throws ProcessingException {
+
    if (userLevel == LEVEL_OWN) {
+
      return SERVICES.getService(ICompanyService.class).isOwnCompany(getCompanyId());
+
    }
+
    return false;
+
  }
+
 
+
  public Long getCompanyId() {
+
  return m_companyId;
+
  }
+
}
+
</source>
+
 
+
In order to enable fine-grained access control so the user can only access her/his personal company objects, the user's permission level must be changed to 10 in the table '''ROLE_PERMISSION.PERMISSION_LEVEL''' for the permission '''ReadCompanyPermission'''.
+
 
+
The access-check to protect the company resource would be changed as follows:
+
 
+
<source lang="java">
+
if (!ACCESS.check(new ReadCompanyPermission(xy))) {
+
  throw new VetoException("Authorization failed");
+
}
+
// user is authorized, do some business logic here
+
</source>
+
 
+
As you may have noticed, there is provided an implicit level in the <code>ReadCompanyPermission</code>'s constructor when doing the super call. This level stands for the minimal required level the user must have in order to access the resource. If you would put '''LEVEL_ALL''' in there, the user would not be allowed to access the company resource anymore as her/his level is only 10 ('''LEVEL_OWN''') which is lower than 100 ('''LEVEL_ALL''').
+
In difference, the level '''LEVEL_UNDEFINED''' (-1) does not represent a concrete level, but exclusively stands for fine-grained access control. As a consequence, the access controller does not decide by itself whether to grant access or not. Thereto, it delegates this decision to you by invoking <code>execCheckLevel(int userLevel)</code> on the permission.
+
 
+
In this example, the provided userLevel would be 10 ('''LEVEL_OWN'''). That indicates your code that the caller is only allowed to see companies which he belongs to.
+
In consequence, you have to verify the user's relation to the given company and grant access accordingly.
+
 
+
So feel free to define some other fine-grained access levels, e.g. '''LEVEL_DEPARTMENT''' = 20;
+
 
+
Please note, that by calling <code>ACCESS.getLevel(Permission p)</code> the user specific access level on that permission can be requested.
+
 
+
 
+
== Permission to see certain Records (Row-Level Granting) ==
+
 
+
To control who is allowed to see certain records, this must be implemented directly in the SQL statement that selects the data from the database into the application. Records for which the logged in user has no right to see, are not returned by the SQL statement. This is only possible if the users, the roles and the permissions are stored in the database.
+

Latest revision as of 07:33, 18 March 2024

The Scout documentation has been moved to https://eclipsescout.github.io/.

Back to the top