ARGO Accounting
This project uses Quarkus, the Supersonic Subatomic Java Framework.
- Java 11+
- Apache Maven 3.8.1+
- Docker (for dev mode)
Quarkus supports the automatic provisioning of unconfigured services in development and test mode. They refer to this capability as Dev Services. From a developer’s perspective this means that if you include an extension and don’t configure it then Quarkus will automatically start the relevant service (usually using Testcontainers behind the scenes) and wire up your application to use this service.
You can run your application in dev mode that enables live coding using:
mvn clean compile quarkus:dev
The application can be packaged using:
mvn clean package
It produces the quarkus-run.jar
file in the target/quarkus-app/
directory.
Be aware that it’s not an über-jar as the dependencies are copied into the target/quarkus-app/lib/
directory.
The application is now runnable using java -jar target/quarkus-app/quarkus-run.jar
.
If you want to build an über-jar, execute the following command:
mvn clean package -Dquarkus.package.type=uber-jar
The application, packaged as an über-jar, is now runnable using java -jar target/*-runner.jar
.
You can create a native executable using:
mvn clean package -Pnative
Or, if you don't have GraalVM installed, you can run the native executable build in a container using:
mvn clean package -Pnative -Dquarkus.native.container-build=true
You can then execute your native executable with: ./target/rest-json-quickstart-1.0.0-SNAPSHOT-runner
If you want to learn more about building native executables, please consult https://quarkus.io/guides/maven-tooling.
When running the production version of the application, the MongoDB connection needs to be configured as normal by setting the connection string in quarkus.mongodb.connection-string
.
If you want to continue use Dev Services we recommend that you use the %prod.
profile to define your MongoDB settings. What that means practically, is that Quarkus
will automatically start a MongoDB container when running tests or dev-mode, and automatically configure the connection.
To access Accounting System API resources, you have to be authenticated by EOSC Core Infrastructure Proxy. These resources are protected and can only be accessed if a client is sending a bearer token along with the request, which must be valid and trusted by the Accounting System API.
The EOSC Core Infrastructure Proxy offers various Identity Providers where the authentication process can be performed.
Once you log in to your preferable Identity Provider, you obtain an access token. Using this token, you can access the available API operations.
When passing in the access token in an HTTP header, you should make a request like the following:
curl http://localhost:8080/accounting-system/metric-definitions
-H "Authorization: Bearer {token}"
There is an ancillary web page at {accounting_system_host}
where you can identify yourself. This page is responsible for :
- redirecting a user to EOSC Core Infrastructure Proxy in order to be authenticated
- displaying the obtained token
The Accounting System API also needs to authenticate services. For this scenario, typical authentication schemes like username + password don't make sense. Instead, the services use the Client Credentials Flow , in which they pass along their Client ID and Client Secret to authenticate themselves and get an access token.
The Client ID and Client Secret are generated by Keycloak and provided by GRNET. Once the service acquires them, it can obtain a token by executing the following HTTP request:
curl --location --request POST 'https://keycloak/auth/realms/einfra/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'client_id=<CLIENT_ID>' \
--data-urlencode 'client_secret=<CLIENT_SECRET>' \
--data-urlencode 'grant_type=client_credentials'
Quarkus starts a Keycloak container for both the dev and/or test modes and initializes them by registering the existing Keycloak realm or creating a new realm with the client and users for you to start developing the Accounting System application secured by Keycloak immediately. More details about how to obtain access tokens in dev mode you can find here.
If you want to integrate with external OIDC Server, you have to remove the prefix %prod.
from %prod.quarkus.oidc.auth-server-url
.
Then you have to configure the following variables in application.properties
:
quarkus.oidc.auth-server-url={The base URL of the OIDC server}
quarkus.oidc.client-id={Specifies an alpha-numeric string that will be used as the client identifier for OIDC requests}
quarkus.oidc.credentials.secret={Client secret which is used for a client_secret_basic authentication method}
You also need to comment out all the properties that start with %dev.quarkus.oidc
.
Collection-level permissions control services’ access to the collection. Collection permissions are configured using roles, and each collection contains a set of roles that allows services to create, read, update and delete entities of that collection. We assign permissions to services based on their roles. A role is a collection of permissions that you can apply to every service. We can assign one or more roles to each service and one or more permissions to each role.
To generate a role, we have to define the available API operations and access types. By combining operations and access types, you can generate permissions and then you can assign these permissions to each role.
The available API operations are:
Operation | Description |
---|---|
Create | Can create new entities within a collection |
Update | Can update existing entities within a collection |
Delete | Can delete existing entities within a collection |
Read | Can fetch existing entities within a collection |
Acl | Can specify which services are granted access to particular entities within a collection |
The following table illustrates the available access types:
Access Type | Description |
---|---|
Always | Services are always able to perform this operation |
Never | Services are never able to perform this operation |
Entity | Services have only access to entities that they have created |
If the service has more than one role :
- If the access types are Always and Entity, the service will have Always access, because this access type is the most permissive and will override the others. In other words, the most permissive access type takes precedence.
- Never always takes precedence over any other access type.
The available Accounting System API collections are:
Collections |
---|
MetricDefinition |
Metric |
Role |
Provider |
Installation |
Based on the above, we can generate some indicative roles:
Role | Create | Update | Delete | Read | Acl | Collection |
---|---|---|---|---|---|---|
metric_definition_admin | Always | Always | Always | Always | Always | MetricDefinition |
metric_inspector | Always | Metric | ||||
metric_creator | Always | Entity | Entity | Entity | Entity | Metric |
role_editor | Always | Always | Never | Always | Role |
Notes:
- The role name should be unique.
- The blank value is converted to "Never" value
Consequently:
- metric_definition_admin can create new Metric Definitions, as well as update, delete and read any entity in the collection. Can also specify which services will be granted access to any entity within a Metric Collection. In other words, metric_definition_admin will always be able to perform any operation in the Metric Definition collection.
- metric_inspector can read any entity in the Metric collection, but cannot create new ones or update, delete any Metrics.
- metric_creator can create new Metrics, but can update, delete or read only its Metrics. It can also specify which services will have access to the entities it has created. Finally, it can also manage Metrics that have been explicitly declared through the ACL process.
- role_editor can create new Roles, as well as update, and read any entity in the Role collection but cannot delete any entity in it.
It is possible to create and manage new roles using the API. It should be noted that only the system_admin role, which is initialized in the API, can create and manage roles through the API. Consequently, it is the only role that can grant access to other roles.
We can create the aforementioned roles by executing the following requests:
- metric_definition_admin
POST 'https://host/accounting-system/roles'
{
"name" : "metric_definition_admin",
"collections_access_permissions":[
{
"collection": "MetricDefinition",
"access_permissions" :[
{
"operation" : "CREATE",
"access_type" : "ALWAYS"
},
{
"operation" : "UPDATE",
"access_type" : "ALWAYS"
},
{
"operation" : "DELETE",
"access_type" : "ALWAYS"
},
{
"operation" : "READ",
"access_type" : "ALWAYS"
},
{
"operation" : "ACL",
"access_type" : "ALWAYS"
}
]
}
]
}
- metric_inspector
POST 'https://host/accounting-system/roles'
{
"name" : "metric_inspector",
"collections_access_permissions":[
{
"collection": "Metric",
"access_permissions" :[
{
"operation" : "READ",
"access_type" : "ALWAYS"
}
]
}
]
}
- metric_creator
POST 'https://host/accounting-system/roles'
{
"name" : "metric_creator",
"collections_access_permissions":[
{
"collection": "Metric",
"access_permissions" :[
{
"operation" : "CREATE",
"access_type" : "ALWAYS"
},
{
"operation" : "UPDATE",
"access_type" : "ENTITY"
},
{
"operation" : "DELETE",
"access_type" : "ENTITY"
},
{
"operation" : "READ",
"access_type" : "ENTITY"
},
{
"operation" : "ACL",
"access_type" : "ENTITY"
}
]
}
]
}
- role_editor
POST 'https://host/accounting-system/roles'
{
"name" : "role_editor",
"collections_access_permissions":[
{
"collection": "Role",
"access_permissions" :[
{
"operation" : "CREATE",
"access_type" : "ALWAYS"
},
{
"operation" : "UPDATE",
"access_type" : "ALWAYS"
},
{
"operation" : "DELETE",
"access_type" : "NEVER"
},
{
"operation" : "READ",
"access_type" : "ALWAYS"
}
]
}
]
}
You can also create a more generic role. For example, the editor can create new Roles, Metrics, and Metric Definitions, as well as update, and read any entity in those collections. But cannot delete any entity:
- editor
POST 'https://host/accounting-system/roles'
{
"name" : "editor",
"collections_access_permissions":[
{
"collection": "Role",
"access_permissions" :[
{
"operation" : "CREATE",
"access_type" : "ALWAYS"
},
{
"operation" : "UPDATE",
"access_type" : "ALWAYS"
},
{
"operation" : "READ",
"access_type" : "ALWAYS"
}
]
},
{
"collection": "Metric",
"access_permissions" :[
{
"operation" : "CREATE",
"access_type" : "ALWAYS"
},
{
"operation" : "UPDATE",
"access_type" : "ALWAYS"
},
{
"operation" : "READ",
"access_type" : "ALWAYS"
}
]
},
{
"collection": "MetricDefinition",
"access_permissions" :[
{
"operation" : "CREATE",
"access_type" : "ALWAYS"
},
{
"operation" : "UPDATE",
"access_type" : "ALWAYS"
},
{
"operation" : "READ",
"access_type" : "ALWAYS"
}
]
}
]
}
The generated roles can be assigned to different users or services via Keycloak.
To deploy the API in a machine :
- First, you have to set up a mongo database (We are currently using
4.0.28
, but other versions may be compatible) - Second, there must be an über-jar version of the API on this machine
Before running the über-jar, you have to export the following variables:
- export QUARKUS_MONGODB_CONNECTION_STRING=mongodb://{host}:{port}
- export SERVER_URL={The proxy server URL that acts on behalf of the Accounting System}
- export QUARKUS_OIDC_AUTH_SERVER_URL={The base URL of the OpenID Connect (OIDC) server. Note if you work with Keycloak OIDC server, make sure the base URL is in the following format: https://host:port/auth/realms/{realm} where {realm} has to be replaced by the name of the Keycloak realm}
- export QUARKUS_OIDC_CLIENT_ID={Specifies an alpha-numeric string that will be used as the client identifier for OIDC requests}
- export QUARKUS_OIDC_CREDENTIALS_SECRET={Client secret which is used for a client_secret_basic authentication method}
Once the variables above have been exported, you should execute the following command:
java -jar *-runner.jar
Once the application is started, you can make a request to the /open-api
endpoint:
curl {accounting_system_host}/open-api
Swagger UI is accessible at /swagger-ui
endpoint:
{accounting_system_host}/swagger-ui