Skip to main content

Notice: This Wiki is now read only and edits are no longer possible. Please see: https://gitlab.eclipse.org/eclipsefdn/helpdesk/-/wikis/Wiki-shutdown-plan for the plan.

Jump to: navigation, search

Difference between revisions of "BaSyx / Documentation / Components / Security / Authorization"

(Created page with "<span style="color:red;font-size:200%">The whole document targets the Java-version of the SDK and components only.</span> You can add authorization requirements to the action...")
 
(No difference)

Revision as of 05:55, 20 July 2022

The whole document targets the Java-version of the SDK and components only.

You can add authorization requirements to the actions of BaSyx components that would be subject to the AssetAdministrationShell, Registry and Submodel APIs.

BaSyx Asset Administration Shell Repository HTTP REST-API

BaSyx Registry HTTP REST-API

BaSyx Submodel HTTP REST-API


As BaSyx is meant to support various architectures as a foundational technology, the authorization capabilities are designed accordingly in a somewhat generic way to make it adaptable. At the same time, to reduce the effort, there are implemented standard strategies shipped with BaSyx.

Provided Authorization Strategies

GrantedAuthority

The name comes from the GrantedAuthority interface of Spring Security.

As a default, a list of such GrantedAuthority objects can be obtained from a Spring Security Authentication object, which in turn can be attached to the Spring Security Context associated to individual requests the BaSyx server receives. For this to work, we need to indicate that the Authentication object should be taken from the JWT Bearer Token included in the Authorization field of an HTTP request and that token should also be validated and checked against the authorization server. The granted authorities are then the scopes inside the JWT. See the table below to for what scopes are required for what actions.

This strategy can further be customized by specifying how the granted authorities are to be obtained (implementing the ISubjectInformationProvider and IGrantedAuthorityAuthenticator interfaces).

For more information on how GrantedAuthority can be used, see "Using Authorization -> Off-the-Shelf components" and "Using Authorization -> SDK" respectively.

SimpleAbac

The SimpleAbac strategy is a bit more flexible than GrantedAuthority in that there are further indirections.

As a default, instead of having the permissions directly held in the JWT, the (Keycloak-formatted)-JWT only indicates roles of the requester and, from another data source, there are rules specified consisting of role, action and resource tuples that decide whether the role is allowed to do the action on the resource.

This strategy can be further customized by specifying how the roles are obtained (implementing the ISubjectInformationProvider and IRoleAuthenticator interfaces).

The rules can also be provided in different ways (implementing the IAbacRuleChecker interface).

Using Authorization

Off-the-Shelf Components

For the Off-the-Shelf components (AASServer and Registry), the creation of the component is already handled for you. The authorization for those can be configured via the properties files.


The authorization key-value pairs inside the files look like this:

For aas.properties:

aas.authorization=Enabled
aas.authorization.strategy=GrantedAuthority
aas.authorization.strategy.jwtBearerTokenAuthenticationConfigurationProvider=org.eclipse.basyx.components.aas.authorization.KeycloakJwtBearerTokenAuthenticationConfigurationProvider
aas.authorization.strategy.jwtBearerTokenAuthenticationConfigurationProvider.keycloak.serverUrl=http://127.0.0.1:9005/auth
aas.authorization.strategy.jwtBearerTokenAuthenticationConfigurationProvider.keycloak.realm=basyx-demo
aas.authorization.strategy.simpleAbac.rulesFilePath=/abac_rules.json
aas.authorization.strategy.simpleAbac.subjectInformationProvider=org.eclipse.basyx.extensions.shared.authorization.JWTAuthenticationContextProvider
aas.authorization.strategy.simpleAbac.roleAuthenticator=org.eclipse.basyx.extensions.shared.authorization.KeycloakRoleAuthenticator
aas.authorization.strategy.grantedAuthority.subjectInformationProvider=org.eclipse.basyx.extensions.shared.authorization.AuthenticationContextProvider
aas.authorization.strategy.grantedAuthority.grantedAuthorityAuthenticator=org.eclipse.basyx.extensions.shared.authorization.AuthenticationGrantedAuthorityAuthenticator


It is analogous for the registry, apart from the keys there being prefixed with "registry" instead of "aas" like
registry.authorization=Enabled
.
BaSyx Asset Administration Shell Repository HTTP REST-API
Property Possible values Description Default value
aas.authorization Disabled, Enabled main switch for authorization features, when disabled, all the other fields won't be effective Disabled
aas.authorization.strategy GrantedAuthority, SimpleAbac The basic authorization strategy, see section "Provided Authorization Strategies" GrantedAuthority
aas.authorization.strategy.jwtBearerTokenAuthenticationConfigurationProvider <class> The class responsible for providing a jwt bearer token authentication configuration, has to implement the IJwtBearerTokenAuthenticationConfigurationProvider interface org.eclipse.basyx.components.aas.authorization.KeycloakJwtBearerTokenAuthenticationConfigurationProvider
aas.authorization.strategy.jwtBearerTokenAuthenticationConfigurationProvider.keycloak.serverUrl <url> base url for the keycloak null
aas.authorization.strategy.jwtBearerTokenAuthenticationConfigurationProvider.keycloak.realm basyx-demo realm in the keycloak null
aas.authorization.strategy.simpleAbac.rulesFilePath <file path> (json, see schema) relative path to abac rules for SimpleAbac strategy /abac_rules.json
aas.authorization.strategy.simpleAbac.subjectInformationProvider <class> class that provides the Authentication object for SimpleAbac strategy, has to implemented ISubjectInformationProvider org.eclipse.basyx.extensions.shared.authorization.JWTAuthenticationContextProvider
aas.authorization.strategy.simpleAbac.roleAuthenticator <class> class that extracts the roles from the Authentication object for SimpleAbac strategy, has to implement IRoleAuthenticator org.eclipse.basyx.extensions.shared.authorization.KeycloakRoleAuthenticator
aas.authorization.strategy.grantedAuthority.subjectInformationProvider <class> class that fetches the Authentication object for GrantedAuthority strategy, hsa to implement ISubjectInformationProvider org.eclipse.basyx.extensions.shared.authorization.AuthenticationContextProvider
aas.authorization.strategy.grantedAuthority.grantedAuthorityAuthenticator <class> class that extracts the granted authorities from Authentication object for GrantedAuthority strategy, has to implement IGrantedAuthorityAuthenticator org.eclipse.basyx.extensions.shared.authorization.AuthenticationGrantedAuthorityAuthenticator

Schema for abac_rules.json:

[
  {
    "role": "admin",
    "action": "urn:org.eclipse.basyx:scope:aas-registry:read",
    "aasId": "*",
    "smId": "*",
    "smElIdShortPath": "*"
  },
  {
    "role": "admin",
    "action": "urn:org.eclipse.basyx:scope:aas-registry:write",
    "aasId": "*",
    "smId": "*",
    "smElIdShortPath": "*"
  },
  {
    "role": "user",
    "action": "urn:org.eclipse.basyx:scope:aas-registry:read",
    "aasId": "*",
    "smId": "*",
    "smElIdShortPath": "*"
  }
]

SDK

When using the SDK directly, you have to mount the desired servlets yourself onto the BaSyx server and create for example a submodel API that should be served on some endpoint. The main types of resources the BaSyx SDK supplies are specified with the following interfaces:

  • IAASAggregator
  • IAASAPI
  • IAASRegistry
  • ISubmodelAggregator
  • ISubmodelAPI

  • and for each of those, there are common implementations available. In order to add authorization functionalities to those, we employ a decorator pattern, wrapping the actual resource providers in authorized decorations, which implement the interface, too, are therefore usable in the same places where common implementations would be allowed.

    So instead of

    context.addServletMapping("/*", new VABHTTPInterface<>(
      new SubmodelProvider(
        new VABSubmodelAPI(
          new SubmodelProvider(
            new Submodel()
          )
        )
      )
    ));


    you would write

    context.addServletMapping("/*", new VABHTTPInterface<>(
      new SubmodelProvider(
        new AuthorizedSubmodelAPI<>(
          new VABSubmodelAPI(
            new SubmodelProvider(
              new Submodel()
            )
          )
        )
      )
    ));


    This overload of the AuthorizedSubmodelAPI constructor is deprecated and would use the GrantedAuthority authorization strategy as a legacy mechanism. The current, more explicit variant would be:

    context.addServletMapping("/*", new VABHTTPInterface<>(
      new SubmodelProvider(
        new AuthorizedSubmodelAPI<>(
          new VABSubmodelAPI(
            new SubmodelProvider(
              new Submodel()
            )
          ),
          new GrantedAuthoritySubmodelAPIAuthorizer<>(
            new AuthenticationGrantedAuthorityAuthenticator()
          ),
          new AuthenticationContextProvider()
        )
      )
    ));


    the signature of the AuthorizedSubmodelAPI constructor being
    (ISubmodelAPI decoratedSubmodelAPI, ISubmodelAPIAuthorizer<SubjectInformationType> submodelAPIAuthorizer, ISubjectInformationProvider<SubjectInformationType> subjectInformationProvider)
    .

    The available authorized variants of the aforementioned interfaces are:

  • AuthorizedAASAggregator
  • AuthorizedAASAPI
  • AuthorizedAASRegistry
  • AuthorizedSubmodelAggregator
  • AuthorizedSubmodelAPI

  • with a similar pattern.

    The decocator does not automatically work transitively. Even if you authorize the AAS-Aggregator, the AAS-APIs mounted therein are not authorized. There are however overloads for the factories of AAS-API you can pass to the AAS-Aggregator constructor, so you can make it create an authorized version of the AAS-API.

    ISubjectInformationProvider<Authentication> subjectInformationProvider = new AuthenticationContextProvider();
    IGrantedAuthorityAuthenticator<Authentication> grantedAuthorityAuthenticator = new AuthenticationGrantedAuthorityAuthenticator();
    context.addServletMapping("/*", new VABHTTPInterface<>(
      new AASAggregatorProvider(
        new AuthorizedAASAggregator<>(
          new AASAggregator(
            aas -> new AuthorizedAASAPI<>(
              new VABAASAPIFactory().getAASApi(aas),
              new GrantedAuthorityAASAPIAuthorizer<>(
                grantedAuthorityAuthenticator
              ),
              subjectInformationProvider
            ),
            submodel -> new AuthorizedSubmodelAPI<>(
              new VABSubmodelAPIFactory().getSubmodelAPI(submodel),
              new GrantedAuthoritySubmodelAPIAuthorizer<>(
                grantedAuthorityAuthenticator
              ),
              subjectInformationProvider
            ),
            new AuthorizedAASRegistryProxy(REGISTRY_URL, keycloakService::getTokenAsBearer)
          ),
          new GrantedAuthorityAASAggregatorAuthorizer<>(
            grantedAuthorityAuthenticator
          ),
          subjectInformationProvider
        )
      )
    ));

    It's analogous for the submodel aggregator. The registry doesn't have that issue.

    In order to support reading a JWT from the HTTP Authorization header and validating the token, we need to further install a JwtBearerTokenAuthenticationConfiguration on the context:

    String KEYCLOAK_SERVER_URL = "http://localhost:9005/auth"; // base auth path of the Keycloak
    String KEYCLOAK_REALM = "basyx-demo"; // realm of the Keycloak which the token belongs to
    KeycloakService keycloakService = new KeycloakService(KEYCLOAK_SERVER_URL, KEYCLOAK_REALM);
    ...
    // context is a BaSyxContext
    context.setJwtBearerTokenAuthenticationConfiguration(
      keycloakService.createJwtBearerTokenAuthenticationConfiguration()
    );

    Customziation

    By implementing ISubmodelAPIAuthorizer or similar plus ISubjectInformationProvider, you can add own authorization logic. The ISubmodelAPIAuthorizer interface for example declares methods which mirror the operations of the ISubmodelAPI interface to deliver an authorization decision for each of those operations.

    The next snippet shows the GrantedAuthority implementation of a method making a decision about getting the list of submodel elements of the submodel API:

    @Override
    public Collection<ISubmodelElement> enforceGetSubmodelElements(final SubjectInformationType subjectInformation, final IIdentifier aasId, final IIdentifier smId, final Supplier<Collection<ISubmodelElement>> smElListSupplier) throws InhibitException {
      if (grantedAuthorityAuthenticator.getAuthorities(subjectInformation).stream()
      .map(GrantedAuthority::getAuthority)
      .noneMatch(authority -> authority.equals(AuthorizedSubmodelAPI.READ_AUTHORITY))) {
        throw new InhibitException();
      }
      return smElListSupplier.get();
    }


    If you want to inhibit an operation, you should throw an InhibitException, which gets translated into a 403 HTTP response status code later on, otherwise just return from the method execution normally. Some operations like the one shown above have a return value, thus you can also modify what value gets returned when the operation is allowed. For example, in this case, it can be used to filter some elements from the list or it could be used to pseudonymize returned data.

    The ISubjectInformationProvider supplies the subject information considered by the authorizer. For example, you can read the JWT from a Spring Security Authentication object from the Spring Security Context. Its generic parameter has to match the generic parameter of the associated ISubmodelAPIAuthorizer.

    Further utility and facilities for authorization can be found at [https://github.com/eclipse-basyx/basyx-java-sdk/tree/main/src/main/java/org/eclipse/basyx/extensions/shared/authorization].

    Among that is the KeycloakService class, which has some methods to interact with a Keycloak server. The BaSyx SDK also has the [Keycloak Admin REST Client Java library] as a dependency.

    Scope tables for GrantedAuthority and SimpleAbac

    Below are reference tables that show which action scopes are used in what endpoints of the Off-the-Shelf components and analogously for the component servlets that you would add with the SDK. The permissions and authorization decisions are not directly matching the endpoints but rather target the individual service operations backing them. For example, in order to write to an AAS-API on an AASServer, it would first ask the AAS-Aggregator to provide the AAS-API, which requires reading permission for the AAS-Aggregator, before trying to write to the AAS-API, which would additionally need writing permissions for the AAS-API.

    BaSyx Asset Administration Shell Repository HTTP REST-API
    Action Used in
    urn:org.eclipse.basyx:scope:aas-aggregator:read
  • GET /shells
  • GET /shells/{aasId}
  • PUT /shells/{aasId}
  • DELETE /shells/{aasId}
  • GET /shells/{aasId}/aas
  • GET /shells
  • GET /shells/{aasId}/aas/submodels
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/values
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodelElements
  • PUT /shells/{aasId}/aas/submodels/{submodelIdShort}/submodelElements/{seIdShortPath}
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodelElements/{seIdShortPath}
  • DELETE /shells/{aasId}/aas/submodels/{submodelIdShort}/submodelElements/{seIdShortPath}
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodelElements/{seIdShortPath}/value
  • PUT /shells/{aasId}/aas/submodels/{submodelIdShort}/submodelElements/{seIdShortPath}/value
  • POST /shells/{aasId}/aas/submodels/{submodelIdShort}/submodelElements/{idShortPathToOperation}/invoke
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodelElements/{idShortPathToOperation}/invocationList
  • urn:org.eclipse.basyx:scope:aas-aggregator:write
  • PUT /shells/{aasId}
  • DELETE /shells/{aasId}
  • PUT /shells/{aasId}/aas/submodels/{submodelIdShort}
  • DELETE /shells/{aasId}/aas/submodels/{submodelIdShort}
  • urn:org.eclipse.basyx:scope:aas-api:read
  • GET /shells/{aasId}
  • GET /shells/{aasId}/aas
  • GET /shells/{aasId}/aas/submodels
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}
  • urn:org.eclipse.basyx:scope:aas-api:write
  • PUT /shells/{aasId}/aas/submodels/{submodelIdShort}
  • DELETE /shells/{aasId}/aas/submodels/{submodelIdShort}
  • urn:org.eclipse.basyx:scope:aas-registry:write
  • GET /api/v1/registry
  • GET /api/v1/registry/{aasId}
  • GET /api/v1/registry/{aasId}/submodels/{submodelId}
  • GET /api/v1/registry/{aasId}/submodels
  • urn:org.eclipse.basyx:scope:aas-registry:write
  • PUT /api/v1/registry/{aasId}
  • DELETE /api/v1/registry/{aasId}
  • PUT /api/v1/registry/{aasId}/submodels/{submodelId}
  • DELETE /api/v1/registry/{aasId}/submodels/{submodelId}
  • urn:org.eclipse.basyx:scope:sm-aggregator:read
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/values
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{seIdShortPath}
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{seIdShortPath}/value
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{idShortPathToOperation}/invocationList
  • PUT /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{seIdShortPath}
  • DELETE /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{seIdShortPath}
  • PUT /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{seIdShortPath}/value
  • POST /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{idShortPathToOperation}/invoke
  • urn:org.eclipse.basyx:scope:sm-aggregator:write Example
    urn:org.eclipse.basyx:scope:sm-api:read
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/values
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{seIdShortPath}
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{seIdShortPath}/value
  • GET /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{idShortPathToOperation}/invocationList
  • urn:org.eclipse.basyx:scope:sm-api:write
  • PUT /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{seIdShortPath}
  • DELETE /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{seIdShortPath}
  • PUT /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{seIdShortPath}/value
  • urn:org.eclipse.basyx:scope:sm-api:execute
  • POST /shells/{aasId}/aas/submodels/{submodelIdShort}/submodel/submodelElements/{idShortPathToOperation}/invoke
  • BaSyx Registry HTTP REST-API
    Action Used in
    urn:org.eclipse.basyx:scope:aas-registry:read
  • GET /api/v1/registry
  • GET /api/v1/registry/{aasId}
  • GET /api/v1/registry/{aasId}/submodels/{submodelId}
  • GET /api/v1/registry/{aasId}/submodels
  • urn:org.eclipse.basyx:scope:aas-registry:write
  • PUT /api/v1/registry/{aasId}
  • DELETE /api/v1/registry/{aasId}
  • PUT /api/v1/registry/{aasId}/submodels/{submodelId}
  • DELETE /api/v1/registry/{aasId}/submodels/{submodelId}
  • BaSyx Submodel HTTP REST-API
    Action Used in
    urn:org.eclipse.basyx:scope:sm-api:read
  • GET /submodel
  • GET /submodel/values
  • GET /submodel/submodelElements
  • GET /submodel/submodelElements/{seIdShortPath}
  • GET /submodel/submodelElements/{seIdShortPath}/value
  • GET /submodel/submodelElements/{idShortPathToOperation}/invocationList
  • urn:org.eclipse.basyx:scope:sm-api:write
  • PUT /submodel/submodelElements/{seIdShortPath}
  • DELETE /submodel/submodelElements/{seIdShortPath}
  • PUT /submodel/submodelElements/{seIdShortPath}/value
  • urn:org.eclipse.basyx:scope:sm-api:execute
  • POST /submodel/submodelElements/{idShortPathToOperation}/invoke
  • Back to the top