Nussknacker has pluggable security architecture - by default we support two type of authentication: BasicAuth and OAuth2. You can either use default authentication provider, based on Basic authentication and static user configuration or integrate with other authentication mechanisms such as custom SSO implementation.
Each user has id and set of permissions for every process category. There are following permissions:
- Read - user can view processes in category
- Write - user can modify/add new processes in category
- Deploy - user can deploy or cancel processes in given category
In addition to permission system oriented around processes' categories we provide additional set of permissions. This feature is designed to control access to components that have no category attached or it doesn't make sense for them to have one.
Currently supported permissions:
- AdminTab - shows Admin tab in the UI (right now there are some useful things kept there including search components functionality).
authentication: {
method: "BasicAuth"
usersFile: "conf/users.conf"
}
users: [
{
identity: "user1"
password: "pass",
roles: ["Writer"]
},
{
identity: "user2"
encryptedPassword: "$2a$12$oA3U7DXkT5eFkyB8GbtKzuVqxUCU0zDmcueBYV218zO/JFQ9/bzY6"
permissions: ["Deployer"]
}
]
rules: [
{
role: "Reader"
permissions: ["Read"]
categories: ["Category1", "Category2"]
},
{
role: "Writer"
permissions: ["Read", "Write"]
categories: ["Category1", "Category2"]
},
{
role: "Deployer"
permissions: ["Read", "Write", "Deploy"]
globalPermissions: ["AdminTab"]
categories: ["Category1", "Category2"]
}
]
Encryption of passwords uses BCrypt algorithm. You can generate sample hash using command:
python -c 'import bcrypt; print(bcrypt.hashpw("password".encode("utf8"), bcrypt.gensalt(rounds=12, prefix="2a")))'
Be aware that usage of BCrypt hashes will cause significant CPU overhead for processing of each http request, because we don't have sessions and all requests are authenticated. To avoid this overhead you can configure cashing of hashes using configuration:
authentication: {
method: "BasicAuth"
usersFile: "conf/users.conf"
cachingHashes {
enabled: true
}
}
This workaround causes that passwords are kept in the memory and it will introduce risk that someone with access to content of heap will see cached passwords.
authentication: {
method: ${?AUTHENTICATION_METHOD} (default: "BasicAuth")
clientSecret: ${?OAUTH2_CLIENT_SECRET}
clientId: ${?OAUTH2_CLIENT_ID}
authorizeUri: ${?OAUTH2_AUTHORIZE_URI}
redirectUri: ${?OAUTH2_REDIRECT_URI}
accessTokenUri: ${?OAUTH2_ACCESS_TOKEN_URI}
profileUri: ${?OAUTH2_PROFILE_URI}
profileFormat: ${?OAUTH2_PROFILE_FORMAT}
implicitGrantEnabled: ${?OAUTH2_IMPLICIT_GRANT_ENABLED}
jwt {
accessTokenIsJwt: ${?OAUTH2_ACCESS_TOKEN_IS_JWT} (default: false)
userinfoFromIdToken: ${?OAUTH2_USERINFO_FROM_ID_TOKEN} (default: false)
publicKey: ${?OAUTH2_JWT_AUTH_SERVER_PUBLIC_KEY}
publicKeyFile: ${?OAUTH2_JWT_AUTH_SERVER_PUBLIC_KEY_FILE}
certificate: ${?OAUTH2_JWT_AUTH_SERVER_CERTIFICATE}
certificateFile: ${?OAUTH2_JWT_AUTH_SERVER_CERTIFICATE_FILE}
idTokenNonceVerificationRequired: ${?OAUTH2_JWT_ID_TOKEN_NONCE_VERIFICATION_REQUIRED}
}
accessTokenParams {
grant_type: ${?OAUTH2_GRANT_TYPE}
}
accessTokenRequestContentType: "application/json" (default)
authorizeParams {
response_type: ${?OAUTH2_RESPONSE_TYPE}
scope: ${?OAUTH2_SCOPE}
audience: ${?OAUTH2_AUDIENCE}
}
headers {
Accept: ${?AUTHENTICATION_HEADERS_ACCEPT}
}
usersFile: ${?AUTHENTICATION_USERS_FILE}
}
When method
is set to OAuth2
, the following fields are mandatory: clientSecret
, clientId
, authorizeUri
, redirectUri
, accessTokenUri
, profileUri
, profileFormat
, implicitGrantEnabled
, usersFile
.
For the profileFormat
one of oidc
or github
is supported by default.
Subconfigs accessTokenParams
, accessTokenRequestContentType
, authorizeParams
, headers
are optional and every field from any of the subconfigs is optional and could be provided separately.
By default access token request is sent using application/json
content type, to use change it (eg. to application/x-www-form-urlencoded
) use accessTokenRequestContentType
config.
Subconfig jwt
is also optional. However, if it is present and enabled
is set to true, idTokenNonceVerificationRequired
and one of the publicKey
, publicKeyFile
, certificate
, certificateFile
fields have to be provided.
-
Setting
jwt.idTokenNonceVerificationRequired
totrue
has an effect only ifimplicitGrantEnabled
is also set totrue
. -
For security reasons, if implicit flow used,
implicitGrantEnabled
andjwt.idTokenNonceVerificationRequired
should be set totrue
. FurthermoreauthorizeParams.response_type
should be set to"access_token id_token"
(not only"access_token"
). Then a receivedaccess_token
format has to be JWT (likeid_token
).Of course, this does not apply when
authorizeParams.response_type
is set tocode
(code flow used). -
Provided
jwt
is enabled, the backend first checks whether a user profile can be obtained from theaccess_token
, secondly it tries to obtain the profile from a request sent toauthorizeUri
.
authentication: {
method: "OAuth2"
clientSecret: ""
clientId: ""
authorizeUri: "https://github.com/login/oauth/authorize"
redirectUri: "http://localhost:3000"
accessTokenUri: "https://github.com/login/oauth/access_token"
profileUri: "https://api.github.com/user"
profileFormat: "github"
implicitGrantEnabled: false
accessTokenParams {
grant_type: "authorization_code"
}
headers {
Accept: "application/json"
}
authorizeParams {
response_type: "code"
}
usersFile: "./src/test/resources/oauth2-users.conf"
}
users: [ //Special settings by user email
{
identity: "[email protected]"
roles: ["Admin"]
}
]
rules: [
{
role: "Admin"
isAdmin: true
},
{
role: "User" //this is default role for all users
permissions: ["Read", "Write", "Deploy"]
categories: ["Defautl", "FraudDetection"]
}
]
We use Auth0 as an example since it is compatible with OIDC.
- "Application Type" should be set to "Regular Web Application"
- "Use Auth0 instead of the IdP to do Single Sign On" should be enabled.
- "Allowed Callback URLs" should contain
redirectUri
. - "Implicit" should be added to "Grant Types" in "Advanced Settings".
More at https://auth0.com/docs/get-started/dashboard/application-settings.
authentication: {
method: "OAuth2"
clientSecret: ""
clientId: ""
authorizeUri: "https://<your-domain>.auth0.com/authorize"
redirectUri: "http://localhost:3000"
accessTokenUri: "https://<your-auth0-domain>.auth0.com/oauth/token"
profileUri: "https://<your-auth0-domain>.auth0.com/userinfo"
profileFormat: "oidc"
implicitGrantEnabled: true
jwt {
accessTokenIsJwt: true
certificateFile: "./etc/cert.pem"
idTokenNonceVerificationRequired: true
}
authorizeParams {
response_type: "access_token id_token"
scope: "openid profile email"
audience: "<your-domain>"
}
usersFile: "./src/test/resources/oauth2-users.conf"
}
OAuth2 backend allows to exchange engine to fetching / parsing data from your OAuth2 Authentication Server and Profile Resource.
By default we support Github data format. To do this, simply replace the OAuth2ServiceFactory by your own implementation.
After that you have to register your implementation using Java's ServiceLoader mechanism by prepare META-INFO/service
resource for pl.touk.nussknacker.ui.security.oauth2.OAuth2ServiceFactory
. You can find an example at tests in
ExampleOAuth2ServiceFactory
file.
You can store passwords as plaintext or (preferably) encrypted using bcrypt. To compute encrypted password you can use following python script:
import bcrypt
print(bcrypt.hashpw("password_to_encode".encode("utf8"), bcrypt.gensalt(rounds = 12, prefix = "2a")))
In order to implement authentication provider you have to implement trait:
trait AuthenticatorFactory {
def createAuthenticator(config: Config, classLoader: ClassLoader): AuthenticationDirective[LoggedUser]
}
It is based on AuthenticationDirective
of Akka Http. Implementation must be put on Nussknacker classpath (Note: not in jar with model)
You must also register your implementation using Java's ServiceLoader mechanism - that is, you have to provide
file META-INF/services/pl.touk.nussknacker.ui.security.api.AuthenticatorFactory
containing fully qualified name of implementation of AuthenticatorFactory
.
Please note that there can be only one implementation on the classpath.