User Tools

Site Tools


development:security

This page describes the details of the security implementation in WebAPI.

DEPENDENCIES

The security subsystem is built on top of Apache Shiro framework.

Apache Shiro is a powerful and easy-to-use Java security framework that performs authentication, authorization, cryptography, and session management. With Shiro’s easy-to-understand API, you can quickly and easily secure any application – from the smallest mobile applications to the largest web and enterprise applications.

It also uses:

  • WAFFLE - for Windows authentication
  • buji-pac4j - for OAuth authentication
  • Java JWT - JSON Web Token for Java and Android

WEBAPI

SECURITY LAYER

The org.ohdsi.webapi.shiro.management.Security abstraction makes it easy to maintain different behaviors of security subsystem. There are two implementations are available out of the box. These are AtlasSecurity and DisabledSecurity. The first handles all the needs of ATLAS application, the latter disables security features.

The default setting in the WebAPI pom.xml is <security.enabled>false</security.enabled> which turns off security by loading the DisabledSecurity module. If you would like to enable security and load the AtlasSecurity module, this can be done by adding <security.enabled>true</security.enabled> to the <profile> section of your settings.xml file as described in the WebAPI Installation Guide. This does require that you rebuild the .war file and redeploy the application.

PATH-BASED SECURITY

The security subsystem is set up by assigning filters to URLs. Before a method underneath the URL is invoked, filters apply some logic. For example, attempt to authenticate user and check if user has permission required to access the method.

AUTHENTICATION

WebAPI itself does not authenticate users, it delegates authentication to external service - Windows Authentication or OAuth provider.

There is a set of authenticating filters which allows to maintain different authentication providers. If authentication is successful, WebAPI responds with token, which then should be used to sign subsequent requests. The token is sent in Bearer header or as a path parameter in case of OAuth.

To sign request put the token prefixed with Bearer keyword into Authorization header

Authorization: Bearer mytokenvalue

The token is JSON Web Token. It is used not only as a proof if identity but also contains some useful information, such as login and user’s permissions. It allows to check permissions on client side without need of further communication with WebAPI.

Here is a list of endpoints related to authentication:

  • /user/login - Windows Authentication endpoint, responds with token within Bearer header
  • /user/oauth/google - Google OAuth, redirects to configured endpoint, putting token as a path parameter
  • /user/oauth/facebook - Facebook OAuth, redirects to configured endpoint, putting token as a path parameter
  • /user/logout - Invalidates token, request should be signed with valid token
  • /user/refresh - Invalidates current token and creates new one with updated permissions and expiration time, request should be signed with valid token

OAUTH SETTINGS

Currently supported OAuth providers are Google and Facebook.

To be able to use API of OAuth service provider you need to obtain API Key and API Secret and put these values into POM file

<security.oauth.google.apiKey>KEY</security.oauth.google.apiKey>
<security.oauth.google.apiSecret>SECRET</security.oauth.google.apiSecret>
<security.oauth.facebook.apiKey>KEY</security.oauth.facebook.apiKey>
<security.oauth.facebook.apiSecret>SECRET</security.oauth.facebook.apiSecret>

ADDING NEW OAUTH PROVIDER

OAuth authentication is handled with buji-pac4j OAuth clients.

To add support of new OAuth service provider into WebAPI, you need to go through following steps.

  • Register application and obtain oauth api credentials
    • Refer to OAuth service provider’s documentation to register application
    • Obtain your API key and secret
    • Adjust redirect URL. This is a URL of a WebAPI endpoint which is responsible for processing provider’s response. The relative path of the endpoint is /user/oauth/callback. The same endpoint is used for all OAuth clients. To distinguish which client is responded add client_name parameter to the path. For example, https://hixbeta.jnj.com:8443/WebAPI/user/oauth/callback?client_name=FacebookClient for Facebook.
  • Add oauth endpoint in WebAPI
    • Create filter for endpoint. Here is a code example for Facebook:
  public class AtlasSecurity extends Security {
    ...
    @Override
    public Map<String, Filter> getFilters() {
      Map<String, javax.servlet.Filter> filters = new HashMap<>();
      ...
      
      // create client
      FacebookClient facebookClient = 
        new FacebookClient(facebookApiKey, facebookApiSecret);
      facebookClient.setScope("email");
      facebookClient.setFields("email");
      
      Config cfg =
        new Config(
          new Clients(
            this.oauthApiCallback
            , googleClient
            , facebookClient        // update config to use new client
          ));
          
      // create filter
      SecurityFilter facebookOauthFilter = new SecurityFilter();
      facebookOauthFilter.setConfig(cfg);
      facebookOauthFilter.setClients("FacebookClient");
      filters.put("facebookAuthc", facebookOauthFilter);
      ...
       
      return filters;
    }
    ...
  }
  • Create endpoint
  public class AtlasSecurity extends Security {
    ...
    
    @Override
    public Map<String, String> getFilterChain() {
      return new FilterChainBuilder()
        ...
        .addOAuthPath("/user/oauth/facebook", "facebookAuthc")
        ...
        .build();
      }
    
    ...
  }
  • Define URL in POM file to redirect after authentication is completed
  <security.oauth.callback.ui>http://localhost:8080/Atlas/#/welcome</security.oauth.callback.ui>

If authentication completes successfully, token is appended to the redirect URL.

BASIC SECURITY CONFIGURATION

In the event that you do not have one of the supported OAuth providers available, WebAPI also supports as basic security configuration as described in this tutorial.

AUTHORIZAION

To access protected method, user must have an appropriate permission. WebAPI checks permissions which are built based on method’s URL and HTTP Method of the request.

For example, request

GET: myservice/myresource/10

corresponds to permission

myservice:myresource:10:get

However, user doesn’t need to have exactly this permission, because SHIRO provides wildcard permissions. It means, that permissions are multilevel and each level may be replaced with a wildcard. Levels are separated by the colon sign and the asterisk sign is used as wildcard character.

For the example above, user may access to resource if he or she has one of the following permissions (assuming that 10 is entity’s ID)

  • * - access to everything
  • myservice - access to all methods of myservice
  • myservice:myresource:*:get - access to any entity of myresource
  • myservice:myresource:10:get - access to entity with ID = 10

So that it’s easy to change level of granularity by giving permissions to users for a whole services or just for certain entities or whatever makes sense.

For details on wildcard permissions please refer to https://shiro.apache.org/static/1.2.3/apidocs/org/apache/shiro/authz/permission/WildcardPermission.html

PROTECT WEBAPI METHODS

To protect certain method, assign appropriate filters to its URL. There is a helper FilterChainBuilder class, which simplifies this. All you need is just to call addProtectedRestPath method passing there URL pattern.

Here is an example of how to protect all methods of user service and generate method of cohortdefinition service

public class AtlasSecurity extends Security {
  ...
  @Override
  public Map<String, String> getFilterChain() {
    return new FilterChainBuilder()
      ...
      .addProtectedRestPath("/user/**")
      .addProtectedRestPath("/cohortdefinition/*/generate/*")
      ...
      .build();
  }
  ...
}

Now request to all endpoints which matching these patterns should be signed with bearer token (see AUTHENTICATION section) and requires permissions corresponding to endpoint's URL (see AUTHORIZAION section).

To protect all methods, use pattern which matches all endpoints:

public class AtlasSecurity extends Security {
  ...
  @Override
  public Map<String, String> getFilterChain() {
    return new FilterChainBuilder()
      ...
      .addProtectedRestPath("/**")
      .build();
  }
  ...
}

ACCESS CONTROL

Permissions are not directly assigned to user. Instead, they are grouped into roles and then roles are assigned to user. This makes access control flexible. You may think about it in terms of roles which user plays in your infrastructure. If user's responsobilities were changed, you can move he or she into another role. Or you can add permissions into role if you realized that this role should involve new activities. All you need is just update records in database by calling appropriate WebAPI method.

Here is a list of tables which security subsystem uses to handle access control:

  • SEC_USER - stores all registered users
  • SEC_USER_ROLE - offers many-to-many relation between users and roles
  • SEC_ROLE - stores roles
  • SEC_ROLE_PERMISSION - offers many-to-many relation between roles and permissions
  • SEC_PERMISSION - stores permissions

ADDING NEW USER

When user logs in firts time, record is created in SEC_USER table. Now user is registered and can be associated with roles.

There are two special roles - public role and personal role. Both are assigned to every newly registred users.

One personal role is created for each new user. The name of personal role is the same as user's login.

This role is intended to hold permissions which are specific to certain user. For example, when user creates new entity, it may be usefull to restrict access to methods which affect the entity, so that only author can change or delete it.

This workflow works when user creates Concept Set, Cohort or Role. Methods which affect these entities are protected. When entity is created, entity-level permissions required for these methods are created as well and then are assigned to creator's personal role.

Public role is intended to hold permissions which should have all users. Initially this role does not contain any permissions, but preveliged users can add permissions into it, so that such permissions will be assigned to all current and future users.

PREDEFINED ROLES

When WebAPI starts first time, it creates a set of roles and permissions. Here is a list:

  • public - intended to contain permissions granted to all users (empty by default)
  • admin - contains permissions required to manage roles and permissions and to add permissions into public role
  • concept set creator - contains permissions required to create concept sets
  • cohort reader - contains permissions required to read cohorts
  • cohort creator - contains permissions required to create cohorts

Each time when WebAPI starts, it looks for configured sources and creates appropriate permissions and roles. One role is created for each source. Roles are named like Source user ({SourceKey}), where {SourceKey} is a value of source.source_key column. For example Source user (CCAE_DEV).

SETTINGS

Settings are available through POM file. All settings related to security are prefixed with security. Here is a full list:

  • security.origin - set client application's origin to make WepAPI methods available from within JavaScript.
  • security.token.expiration - token expiration time in seconds
  • security.ssl.enabled - if set to true only HTTPS requests will be processed
  • security.ssl.port - port for SSL connection
  • security.oauth.callback.ui - URL of page in client application to redirect after OAuth authentication is completed
  • security.oauth.callback.api - URL of WebAPI's endpoint to handle response from OAuth provider. Should end with /user/oauth/callback.
  • security.oauth.google.apiKey - Google OAuth API key
  • security.oauth.google.apiSecret - Google OAuth API secret
  • security.oauth.facebook.apiKey - Facebook OAuth API key
  • security.oauth.facebook.apiSecret - Facebook OAuth API secret

SSL CONFIGURATION IN TOMCAT

First you will need to obtain and install a certificate. Here are steps to achive this:

  • Generate keystore using keytool utillity
keytool -genkey -alias webapi -keyalg RSA -keystore C:\path\to\my\keystore.jks -keysize 2048
  • Generate CSR (Certificate Signing Request)
keytool -certreq -alias webapi -keystore C:\path\to\my\keystore.jks -file C:\path\to\csr\webapi.csr
  • Now you need to follow instructions of CA (Certification Authority) of your choice to submit your CSR (for example VeriSign) to obtain the SSL certifiate. Alternatively, you may opt to use OpenSSL to create a local CA for non-production use.
  • When you obtained certificate from CA, you need to import the CA’s root and intermediate certificates into a Java keystore file before you import the actual SSL certificate. The steps below outline this process for a Windows machine.

Step 1: Retrieving your Root, Intermediate directly from your SSL certificate:

  • Take your SSL certificate with a .cer extension. and double click on it.
  • At the certificate popup click on Certification Path.
  • Under Certification Path you will see a tier of three certificates.
    • Top certificate = Root
    • Middle certificate = Intermediate
    • Bottom certificate = SSL certificate for the common name it was issued to.
  • Double click on the top certificate Root in order to bring up its information.
  • Click Details.
  • Click Copy to File…
  • The Certificate Export Wizard will popup. Click Next.
  • Select Base-64 encoded X.509 (.cer).
  • Click Next.
  • On the next screen click Browse and specify the filename and path you want to save your Root certificate. Name it whatever you desire. Example: Root.cer
  • Click Save.
  • You will be taken back to the previous screen with the location and path of this file specified next to browse. Click Next.
  • Complete the export wizard by clicking Finish.

Step 2: Retrieving your Intermediate CA directly from your SSL certificate (Continued)

You will perform the same steps as above in step 1 but with the middle certificate (Intermediate).

  • 1. Go back to your SSL certificate under the Certification Path tab and double click on the Middle Certificate Intermediate in the tier.
  • Click Details.
  • Click Copy to File…
  • The Certificate Export Wizard will popup. Click Next.
  • Select Base-64 encoded X.509 (.cer).
  • Click Next.
  • On the next screen click Browse and specify the filename and path you want to save your Intermediate certificate. Name it whatever you desire. Example: Intermediate.cer
  • Click Save.
  • You will be taken back to the previous screen with the location and path of this file specified next to browse. Click Next.
  • Complete the export wizard by clicking Finish.

You should now have three files:

  1. Your Root Certificate.
  2. Your Intermediate Certificate.
  3. Your SSL certificate where both the Root, and Intermediate are derived from.

Step 3: Step 3: Installing your Root CA, CA Intermediate, and your SSL certificate

Import the Root Certificate first. You will specify your own alias for this import Example: Root.

keytool -import -alias root -trustcacerts -file C:\path\to\root.cer -keystore C:\path\to\my\keystore.jks  

Import the Intermediate CA certificate second. You will specify your own alias for this import. Example: Intermediate.

keytool -import -alias intermediate -trustcacerts -file C:\path\to\intermediate.cer -keystore C:\path\to\my\keystore.jks

Lastly, import the actual SSL certificate into the keystore.

keytool -importcert -trustcacerts -alias webapi -file C:\path\to\cert\webapi.p7b -keystore C:\path\to\my\keystore.jks

Now you can add SSL connector in Tomcat's server.xml file

<Connector 
  port="8443" 
  protocol="org.apache.coyote.http11.Http11NioProtocol"
  maxThreads="200"
  scheme="https" 
  secure="true" 
  SSLEnabled="true"
  keyAlias="webapi"
  keystoreFile="C:\path\to\my\keystore.jks" 
  keystorePass="{Your keystore password}"
  clientAuth="false" 
  sslProtocol="TLS"/>

Here you may find more details on SSL configuration in Tomcat.

ATLAS

ACCESS TO UI ELEMENTS

When user accesses some sections of ATLAS or press some buttons it results in calling WebAPI methods. Some of them are protected and user may not have appropriate permission. In this case error is generated. To prevent this ATLAS shows UI controls based on user’s permissions.

If user tries to access protected section and he or she isn’t authenticated, the following screen will be shown

If user is authenticated but doesn’t have permissions required for the section, the following screen will be shown

Also some UI controls (such as buttons, checkboxes, etc.) are disabled if user isn’t permitted for corresponding action. For example, if user isn’t permitted to create cohort, the “New Cohort” button is disabled.

CHECKING USER’S PERMISSIONS

Some UI controls may correspond to set of WebAPI calls. To ensure that enabling this UI control is safe permissions must be checked for all required methods. For example, to update Concept Set ATLAS performs two requests

POST: conceptset/{conceptsetId}
POST: conceptset/{conceptsetId}/items

where {conceptsetId} is ID of updated Concept Set.

To address this AuthAPI.js contains set of isPermitted… methods to check all necessary permissions required to perform an atomic (in terms of ATLAS) actions. For the above example it is

isPermittedUpdateConceptset(conceptsetId)

AuthAPI.js also contains isAuthenticated() method to check if access token is presented and not expired.

Note that these permission checks are performed against information contained in the token itself and it may be outdated at the time of checking. This means, that there is still a chance that ATLAS request will be refused by WebAPI with Unauthorized or Forbidden error. To handle such errors, you may use handleAccessDenied(error) method

$.ajax({
  ...
  error: authApi.handleAccessDenied,
  ...
});

To update the token use refreshToken() method.

SIGNING REQUESTS TO PROTECTED METHODS

Client application should sign request with access token to be able to call protected methods. This means that Authorization header must be set. To obtain valid Authorization header use getAuthorizationHeader() method of AuthAPI.js.

In case of JQuery AJAX call it may look like this

$.ajax({
  ...
  headers: {
    Authorization: authApi.getAuthorizationHeader()
  },
  ...
  
});

MANAGE ROLES AND PERMISSIONS

Permissions are grouped into roles. Administrator can assign role to user. On ‘Configuration’ page you may find ‘Manage Permissions’ button. It opens ‘Roles’ page.

Note that ‘Configuration’ page available only for members of ‘admin’ role.

 Roles page

You may click on certain role to edit it or press ‘New Role’ button to create new role.

Now you're on 'Role' page. Select users which are participated in the role on ‘Users’ tab.

 Role page, Users tab

To define permissions for role members, go to ‘Permissions’ tab.

 Role page, Permissions tab

Note that users can’t create permissions - all necessary permissions are created automatically.

development/security.txt · Last modified: 2018/03/20 13:33 by anthonysena