Disable automatic propagation from security providers (#2357)

* Security providers no long automatically propagate.
* Updated security provider documentation

Signed-off-by: Tomas Langer <tomas.langer@oracle.com>
This commit is contained in:
Tomas Langer
2020-09-30 18:18:45 +02:00
committed by GitHub
parent 6881ec8787
commit 7f1a1ad865
47 changed files with 2229 additions and 916 deletions

View File

@@ -0,0 +1,234 @@
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2020 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
=== ABAC Provider
:description: Helidon Security ABAC Provider
:keywords: helidon, security, authorization, abac
Attribute based access control authorization provider.
==== Setup
[source,xml]
.Maven dependency
----
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-abac</artifactId>
</dependency>
----
[source,text]
.Provider class name
----
io.helidon.security.providers.abac.AbacProvider
----
[source,text]
.Provider configuration key
----
abac
----
==== Example code
https://github.com/oracle/helidon/tree/master/examples/security/attribute-based-access-control[]
[source,yaml]
.Configuration example
----
security:
providers:
- abac:
----
==== Configuration options
The following table shows all configuration options of the provider and their default values
[cols="2,2,5"]
|===
|key |default value |description
|`fail-on-unvalidated` |`true` |"Unvalidated" means: an attribute is defined, but there is no validator available for it
|`fail-if-none-validated` |`true` |"None validated" means: there was not a single attribute that was validated
|===
==== How does it work?
ABAC uses available validators and validates them against attributes of the authenticated
user.
Combinations of `fail-on-unvalidated` and `fail-if-none-validated`:
1. `true` & `true`: Will fail if any attribute is not validated and if any has failed validation
2. `false` & `true`: Will fail if there is one or more attributes present and NONE of them is validated or if any has failed validation,
Will NOT fail if there is at least one validated attribute and any number of not validated attributes (and NONE failed)
3. `false` & `false`: Will fail if there is any attribute that failed validation,
Will NOT fail if there are no failed validation or if there are NONE validated
Any attribute of the following objects can be used:
- environment (such as time of request) - e.g. env.time.year
- subject (user) - e.g. subject.principal.id
- subject (service) - e.g. service.principal.id
- object (must be explicitly invoked by developer in code, as object cannot be automatically added to security context) - e.g. object.owner
This provider checks that all defined ABAC validators are validated.
If there is a definition for a validator that is not checked,
the request is denied (depending on configuration as mentioned above).
ABAC provider also allows an object to be used in authorization process, such
as when evaluating if an object's owner is the current user.
The following example uses the Expression language validator to demonstrate the point
in a JAX-RS resource:
[source,java]
.Example of using an object
----
@Authenticated
@Path("/abac")
public class AbacResource {
@GET
@Authorized(explicit = true)
@PolicyStatement("${env.time.year >= 2017 && object.owner == subject.principal.id}")
public Response process(@Context SecurityContext context) {
// probably looked up from a database
SomeResource res = new SomeResource("user");
AuthorizationResponse atzResponse = context.authorize(res);
if (atzResponse.isPermitted()) {
//do the update
return Response.ok().entity("fine, sir").build();
} else {
return Response.status(Response.Status.FORBIDDEN)
.entity(atzResponse.getDescription().orElse("Access not granted"))
.build();
}
}
}
----
*The following validators are implemented:*
* <<Role Validator,Roles>>
* <<Scope Validator,Scopes>>
* <<Expression Language Policy Validator,EL Policy>>
==== Role Validator
Checks whether user/service is in either of the required role(s).
Configuration Key: `role-validator`
Annotations: `@RolesAllowed`, `@RoleValidator.Roles`
[source,yaml]
.Configuration example for `WebServer`
----
security:
web-server.paths:
- path: "/user[/{*}]"
roles-allowed: ["user"]
----
[source,java]
.JAX-RS example
----
@RolesAllowed("user")
@RoleValidator.Roles(value = "service_role", subjectType = SubjectType.SERVICE)
@Authenticated
@Path("/abac")
public class AbacResource {
}
----
===== Interaction with JAX-RS sub-resource locators
When using sub-resource locators in JAX-RS, the roles allowed are collected from each "level" of
execution:
- Application class annotations
- Resource class annotations + resource method annotations
- Sub-resource class annotations + sub-resource method annotations
- Sub-resource class annotations + sub-resource method annotations (for every sub-resource on the path)
The `RolesAllowed` or `Roles` annotation to be used is the last one in the path as defined above.
_Example 1:_
There is a `RolesAllowed("admin")` defined on a sub-resource locator resource class.
In this case the required role is `admin`.
_Example 2:_
There is a `RolesAllowed("admin")` defined on a sub-resource locator resource class and
a `RolesAllowed("user")` defined on the method of the sub-resource that provides the response.
In this case the required role is `user`.
==== Scope Validator
Checks whether user has all the required scopes.
Configuration Key: `scope-validator`
Annotations: `@Scope`
[source,yaml]
.Configuration example for `WebServer`
----
security:
web-server.paths:
- path: "/user[/{*}]"
abac.scopes:
["calendar_read", "calendar_edit"]
----
[source,java]
.JAX-RS example
----
@Scope("calendar_read")
@Scope("calendar_edit")
@Authenticated
@Path("/abac")
public class AbacResource {
}
----
==== Expression Language Policy Validator
Policy executor using Java EE policy expression language (EL)
Configuration Key: `policy-javax-el`
Annotations: `@PolicyStatement`
Example of a policy statement: `${env.time.year >= 2017}`
[source,yaml]
.Configuration example for `WebServer`
----
security:
web-server.paths:
- path: "/user[/{*}]"
policy:
statement: "hasScopes('calendar_read','calendar_edit') AND timeOfDayBetween('8:15', '17:30')"
----
[source,java]
.JAX-RS example
----
@PolicyStatement("${env.time.year >= 2017}")
@Authenticated
@Path("/abac")
public class AbacResource {
}
----

View File

@@ -0,0 +1,111 @@
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2020 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
=== Google Login Provider
:description: Helidon Security Google Login Provider
:keywords: helidon, security, google
Authenticates a token from request against Google identity provider
==== Setup
[source,xml]
.Maven dependency
----
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-google-login</artifactId>
</dependency>
----
[source,text]
.Provider class name
----
io.helidon.security.providers.google.login.GoogleTokenProvider
----
[source,text]
.Provider configuration key
----
google-login
----
==== Example code
https://github.com/oracle/helidon/tree/master/examples/security/google-login[]
[source,yaml]
.Configuration example
----
security:
providers:
- provider:
client-id: "Google client id"
----
==== Configuration options
The following table shows all configuration options of the provider and their default values
[cols="2,2,5"]
|===
|key |default value |description
|`client-id` |{nbsp} |Client id of an application. To create an application, use
the Google developer console (https://developers.google.com/identity/sign-in/web/sign-in)
|`optional` |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is
an important distinction when more than one provider is used
|`realm` |`helidon` |Realm used in the challenge when authentication is not provided and it is required
|`proxy-host` |none |Configuration of a proxy host to use when authenticating the user
|`proxy-port` |`80` |Proxy port
|`token` |`Authorization` header with `bearer` prefix |Configuration of the location of the token (see `TokenHandler`)
|`outbound` |{nbsp} |A list of outbound configurations
|`outbound.*.name` |{nbsp} |Required name of outbound configuration
|`outbound.*.username` |{nbsp} |Optional username used for outbound security; if not provided, current identity is propagated
|`outbound.*.password` |{nbsp} |Optional password used for outbound security
|`outbound.*.transports` |any transport |An array of transports this outbound configuration should be used for
|`outbound.*.hosts` |any host |An array of hosts this outbound configuration should be used for, can be a regular expression
|`outbound.*.paths` |any path |An array of paths this outbound configuration should be used for (such as `/greet`), can be a regular expression
|`outbound.*.methods` |any method |An array of HTTP methods this outbound configuration should be used for
|===
==== How does it work?
We expect to receive a token (with sufficient scopes) from the inbound request,
such as when using the Google login button on a page.
The page has access to the token in javascript and can send it to backend with
every request in a header field (`Authorization` with `bearer ` prefix is assumed by default).
Once we receive the token in Helidon, we parse it and:
1. Validate if it timed out locally
2. Return a cached response (see `EvictableCache` with default values)
3. Otherwise verify using Google API - `GoogleIdTokenVerifier`
We build a subject from the Google token with the following attributes filled (if in token):
- userId
- email
- name
- emailVerified
- locale
- family_name
- given_name
- picture (URL)
*Outbound security*
The token will be propagated to outbound calls if an outbound target exists
that matches the invoked endpoint (see `outbound` configuration above).

View File

@@ -0,0 +1,122 @@
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2018, 2020 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
=== Header Authentication Provider
:description: Helidon Security Header Provider
:keywords: helidon, security, header
Asserts user or service identity based on a value of a header.
==== Setup
[source,xml]
.Maven dependency
----
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-header</artifactId>
</dependency>
----
[source,text]
.Provider class name
----
io.helidon.security.providers.header.HeaderAtnProvider
----
[source,text]
.Provider configuration key
----
header-atn
----
==== Example code
[source,yaml]
.Configuration example
----
security:
providers:
header-atn:
atn-token:
header: "X-AUTH-USER"
outbound:
- name: "internal-services"
hosts: ["*.example.org"]
# propagates the current user or service id using the same header as authentication
- name: "partner-service"
hosts: ["*.partner.org"]
# propagates an explicit username in a custom header
username: "service-27"
outbound-token:
header: "X-Service-Auth"
----
==== Configuration options
The following table shows all configuration options of the provider and their default values
[cols="2,2,5"]
|===
|key |default value |description
|`optional` |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is
an important distinction when more than one provider is used
|`authenticate` |`true` |If set to `false`, authentication will not be attempted (outbound security can still be used)
|`propagate` |`false` |If explicitly set to `false`, identity propagation will not be done. Otherwise it is done if an `outbound`
section is configured
|`principal-type` |`USER` |Can be `USER` or `SERVICE`
|`atn-token` |`none` | Token extraction and propagation, you can define which header to use and how to extract it
|`outbound` |{nbsp} |A list of outbound configurations
|`outbound.*.name` |{nbsp} |Required name of outbound configuration
|`outbound.*.username` |{nbsp} |Optional username used for outbound security; if not provided, current identity is propagated
|`outbound.*.transports` |any transport |An array of transports this outbound configuration should be used for
|`outbound.*.hosts` |any host |An array of hosts this outbound configuration should be used for, can be a regular expression
|`outbound.*.paths` |any path |An array of paths this outbound configuration should be used for (such as `/greet`), can be a regular expression
|`outbound.*.methods` |any method |An array of HTTP methods this outbound configuration should be used for
|`outbound.*.outbound-token` |same as `atn-token` |Configuration of outbound header used to propagate
|`outbound.*.outbound-token.header` |{nbsp} |Name of the header used to propagate the token
|`outbound.*.outbound-token.prefix` |{nbsp} |Prefix for the header value, such as `"username "` (only one of `prefix`, `regexp` and `format` should be defined, `regexp` wins over `prefix`, `format` wins over `regexp`)
|`outbound.*.outbound-token.format` |{nbsp} |String format with a single parameter to create the header value, such as `"username %1s"`
|`outbound.*.outbound-token.regexp` |{nbsp} |Regular expression to create the header value, such as `"username (.*)"`
|===
==== How does it work?
This provider inspects a specified request header and extracts the username/service name from it and
asserts it as current subject's principal.
This can be used when we use perimeter authentication (e.g. there is a gateway that takes
care of authentication and propagates the user in a header).
*Identity propagation*
Identity is propagated only if an outbound target matches the target service.
The following options exist when propagating identity:
1. We propagate the current username using the configured header
2. We use username associated with an outbound target (see example configuration above)
*Caution*
When using this provider, you must be sure the header cannot be explicitly configured by a user or another service.
All requests should go through a gateway that removes this header from inbound traffic, and only configures it for
authenticated users/services.
Another option is to use this with fully trusted parties (such as services within a single company, on a single
protected network not accessible to any users), and of course for testing and demo purposes.

View File

@@ -0,0 +1,144 @@
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2018, 2020 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
=== HTTP Basic Authentication Provider
:description: Helidon Security HTTP Basic Provider
:keywords: helidon, security, basic
HTTP Basic authentication support
==== Setup
[source,xml]
.Maven dependency
----
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-http-auth</artifactId>
</dependency>
----
[source,text]
.Provider class name
----
io.helidon.security.providers.httpauth.HttpBasicAuthProvider
----
[source,text]
.Provider configuration key
----
http-basic-auth
----
==== Example code
https://github.com/oracle/helidon/tree/master/examples/security/outbound-override[]
[source,yaml]
.Configuration example
----
security:
providers:
- http-basic-auth:
realm: "helidon"
users:
- login: "john"
password: "${CLEAR=password}"
roles: ["admin"]
- login: "jack"
password: "password"
roles: ["user", "admin"]
outbound:
- name: "internal-services"
hosts: ["*.example.org"]
# Propagates current user's identity or identity from request property
outbound-token:
header: "X-Internal-Auth"
- name: "partner-service"
hosts: ["*.partner.org"]
# Uses this username and password
username: "partner-user-1"
password: "${CLEAR=password}"
----
==== Configuration options
The following table shows all configuration options of the provider and their default values
[cols="2,2,5"]
|===
|key |default value |description
|`realm` |`helidon` |The realm shown in challenge when user accesses a service without authentication
|`principal-type` |`USER` |Type of authenticated entity - either `USER` or `SERVICE`, can be used in combination with
other authentication mechanism to authenticate both the user (as in person sitting in front of a computer)
and a service (as in the application requesting this service on user's behalf)
|`users` |{nbsp} |List of users when using configuration based approach. As an alternative, you can implement a java service (see below).
|`outbound` |{nbsp} |A list of outbound configurations
|`outbound.*.name` |{nbsp} |Required name of outbound configuration
|`outbound.*.username` |{nbsp} |Optional username used for outbound security; if not provided, current identity is propagated
|`outbound.*.password` |{nbsp} |Optional password used for outbound security
|`outbound.*.transports` |any transport |An array of transports this outbound configuration should be used for
|`outbound.*.hosts` |any host |An array of hosts this outbound configuration should be used for, can be a regular expression
|`outbound.*.paths` |any path |An array of paths this outbound configuration should be used for (such as `/greet`), can be a regular expression
|`outbound.*.methods` |any method |An array of HTTP methods this outbound configuration should be used for
|`outbound.*.outbound-token` |`Authorization` header with `basic` prefix |Configuration of outbound header used to propagate
|`outbound.*.outbound-token.header` |{nbsp} |Name of the header used to propagate the token
|`outbound.*.outbound-token.prefix` |{nbsp} |Prefix for the header value, such as `"basic "` (only one of `prefix`, `regexp` and `format` should be defined, `regexp` wins over `prefix`, `format` wins over `regexp`)
|`outbound.*.outbound-token.format` |{nbsp} |String format with a single parameter to create the header value, such as `"basic %1s"`
|`outbound.*.outbound-token.regexp` |{nbsp} |Regular expression to create the header value, such as `"basic (.*)"`
|===
==== How does it work?
See https://tools.ietf.org/html/rfc7617[].
*Authentication of request*
When a request is received without the `Authorization: basic ....` header, a challenge is returned to provide such
authentication.
When a request is received with the `Authorization: basic ....` header, the username and password is validated
against configured users (and users obtained from custom service if any provided).
Subject is created based on the username and roles provided by the user store.
*Identity propagation*
When identity propagation is configured, there are several options for identifying username and password to propagate:
1. We propagate the current username and password (inbound request must be authenticated using basic authentication).
2. We use username and password from an explicitly configured property (See `HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_USER`
and `HttpBasicAuthProvider.EP_PROPERTY_OUTBOUND_PASSWORD`)
3. We use username and password associated with an outbound target (see example configuration above)
Identity is propagated only if:
1. There is an outbound target configured for the endpoint
2. Or there is an explicitly configured username/password for the current request (through request property)
*Custom user store*
Java service loader service `io.helidon.security.providers.httpauth.spi.UserStoreService` can be implemented to provide
users to the provider, such as when validated against an internal database or LDAP server.
The user store is defined so you never need the clear text password of the user.
_Warning on security of HTTP Basic Authenticaton (or lack thereof)_
Basic authentication uses base64 encoded username and password and passes it over the network. Base64 is only encoding,
not encryption - so anybody that gets hold of the header value can learn the actual username and password of the user.
This is a security risk and an attack vector that everybody should be aware of before using HTTP Basic Authentication.
We recommend using this approach only for testing and demo purposes.

View File

@@ -0,0 +1,109 @@
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2018, 2020 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
=== HTTP Digest Authentication Provider
:description: Helidon Security HTTP Digest Provider
:keywords: helidon, security, digest
HTTP Digest authentication support
==== Setup
[source,xml]
.Maven dependency
----
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-http-auth</artifactId>
</dependency>
----
[source,text]
.Provider class name
----
io.helidon.security.providers.httpauth.HttpDigestAuthProvider
----
[source,text]
.Provider configuration key
----
http-digest-auth
----
==== Example code
[source,yaml]
.Configuration example
----
security:
providers:
- http-digest-auth:
realm: "helidon"
server-secret: "${CLEAR=service-wide-secret-not-known-outside}"
users:
- login: "john"
password: "${CLEAR=password}"
roles: ["admin"]
- login: "jack"
password: "password"
roles: ["user", "admin"]
----
==== Configuration options
The following table shows all configuration options of the provider and their default values
[cols="2,2,5"]
|===
|key |default value |description
|`realm` |`helidon` |The realm shown in challenge when user accesses a service without authentication
|`principal-type` |`USER` |Type of authenticated entity - either `USER` or `SERVICE`, can be used in combination with
other authentication mechanism to authenticate both the user (as in person sitting in front of a computer)
and a service (as in the application requesting this service on user's behalf)
|`users` |{nbsp} |List of users when using configuration based approach. As an alternative, you can implement a java service (see below).
|`algorithm` |`MD5` |Only `MD5` supported
|`nonce-timeout-millis` |1 day |Number of milliseconds for the nonce timeout
|`server-secret` |random |A string to use as a server secret - this is to use digest auth between multiple servers (e.g. when in a cluster). Used to encrypt nonce. This must not be known outside of this app, as others may create digest requests we would trust.
|`qop` |`NONE` |only `AUTH` supported. If left empty, uses the legacy approach (older RFC version). `AUTH-INT` is not supported.
|===
==== How does it work?
See https://tools.ietf.org/html/rfc7616[].
*Authentication of request*
When a request is received without the `Authorization: digest ....` header, a challenge is returned to provide such
authentication using `WWW-Authenticate` header.
When a request is received with the `Authorization: digest ....` header, the request is validated
against configured users (and users obtained from custom service if any provided).
Subject is created based on the username and roles provided by the user store.
*Custom user store*
Java service loader service `io.helidon.security.providers.httpauth.spi.UserStoreService` can be implemented to provide
users to the provider, such as when validated against an internal database or LDAP server.
The user store is defined so you never need the clear text password of the user.
_Note on security of HTTP Digest Authenticaton_
These authentication schemes
should be _obsolete_, though they provide a very easy way to test a protected resource.

View File

@@ -0,0 +1,146 @@
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2020 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
=== HTTP Signatures Provider
:description: Helidon Security HTTP Signatures Provider
:keywords: helidon, security
Support for HTTP Signatures.
==== Setup
[source,xml]
.Maven dependency
----
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-http-sign</artifactId>
</dependency>
----
[source,text]
.Provider class name
----
io.helidon.security.providers.httpsign.HttpSignProvider
----
[source,text]
.Provider configuration key
----
http-signatures
----
==== Example code
https://github.com/oracle/helidon/tree/master/examples/security/webserver-signatures[]
[source,yaml]
.Configuration example
----
security:
providers:
- http-signatures:
inbound:
keys:
- key-id: "service1-hmac"
principal-name: "Service1 - HMAC signature"
hmac.secret: "${CLEAR=somePasswordForHmacShouldBeEncrypted}"
- key-id: "service1-rsa"
principal-name: "Service1 - RSA signature"
public-key:
keystore:
resource.path: "src/main/resources/keystore.p12"
passphrase: "password"
cert.alias: "service_cert"
outbound:
- name: "service2-hmac"
hosts: ["localhost"]
paths: ["/service2"]
signature:
key-id: "service1-hmac"
hmac.secret: "${CLEAR=somePasswordForHmacShouldBeEncrypted}"
- name: "service2-rsa"
hosts: ["localhost"]
paths: ["/service2-rsa.*"]
signature:
key-id: "service1-rsa"
private-key:
keystore:
resource.path: "src/main/resources/keystore.p12"
passphrase: "password"
key.alias: "myPrivateKey"
----
==== Configuration options
The following table shows all configuration options of the provider and their default values
[cols="2,2,5"]
|===
|key |default value |description
|`optional` |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is
an important distinction when more than one provider is used
|`realm` | `helidon` |Realm used for challenge when request does not have a signature
|`headers` | `[SIGNATURE,AUTHORIZATION]` |Headers to look for inbound signatures and to store outbound signatures
|`sign-headers` | `always = ["date"]` |Headers to be signed
|`sign-headers.*.method` |default for all methods |Method this configuration is valid for
|`sign-headers.*.always` | {nbsp} |Array of headers to be always required in the request signature
|`sign-headers.*.if-present` |{nbsp} |Array of headers to be part of the signatures if present in the request
|`inbound` |{nbsp} |Configuration of inbound traffic for authenticating incoming requests
|`inbound.keys` |{nbsp} |Configuration of signature keys to verify incoming requests
|`inbound.keys.*.key-id` |{nbsp} |Key id as used in inbound signature to find the correct certificate/hmac configuration to verify the signature
|`inbound.keys.*.principal-name` |{nbsp} |The principal name (or user name) asserted when the signature is valid
|`inbound.keys.*.principal-type` |`SERVICE` |The type of principal to assert (can be `USER`)
|`inbound.keys.*.algorithm` |according to other configuration |`hmac-sha256` or `rsa-sha256` is assumed if other configuration options for that type are set
|`inbound.keys.*.hmac.secret` |{nbsp} |Secret shared by the service that signed the request and this service for `hmac-sha256` algorithm
|`inbound.keys.*.public-key` |{nbsp} |Public key configuration, implies `rsa-sha256` algorithm
|`inbound.keys.*.public-key.keystore` |{nbsp} |Keystore configuration for public key - full configuration as defined by `KeyStore` class
|`outbound` |{nbsp} |A list of outbound configurations
|`outbound.*.name` |{nbsp} |Required name of outbound configuration
|`outbound.*.username` |{nbsp} |Optional username used for outbound security; if not provided, current identity is propagated
|`outbound.*.password` |{nbsp} |Optional password used for outbound security
|`outbound.*.transports` |any transport |An array of transports this outbound configuration should be used for
|`outbound.*.hosts` |any host |An array of hosts this outbound configuration should be used for, can be a regular expression
|`outbound.*.paths` |any path |An array of paths this outbound configuration should be used for (such as `/greet`), can be a regular expression
|`outbound.*.methods` |any method |An array of HTTP methods this outbound configuration should be used for
|`outbound.*.signature` |{nbsp} |Configuration related to outbound signature configuration
|`outbound.*.signature.key-id` |{nbsp} |Key id to use in the outbound signature (to map to appropriate public key in target service's configuration)
|`outbound.*.signature.hmac.secret` |{nbsp} |Shared secret for hmac
|`outbound.*.signature.private-key` |{nbsp} |Private key configuration for rsa based signatures
|`outbound.*.signature.private-key.keystore` |{nbsp} |Keystore configuration for private key - full configuration as defined by `KeyStore` class
|===
==== Signature basics
* standard: based on https://tools.ietf.org/html/draft-cavage-http-signatures-03
* key-id: an arbitrary string used to locate signature configuration - when a
request is received the provider locates validation configuration based on this
id (e.g. HMAC shared secret or RSA public key). Commonly used meanings are: key
fingerprint (RSA); API Key
==== How does it work?
*Inbound Signatures*
We act as a server and another party is calling us with a signed HTTP request.
We validate the signature and assume identity of the caller.
*Outbound Signatures*
We act as a client and we sign our outgoing requests.
If there is a matching `outbound` target specified in configuration,
its configuration will be applied for signing the outgoing request,
otherwise there is no signature added

View File

@@ -0,0 +1,94 @@
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2020 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
=== IDCS Role Mapper
:description: Helidon Security IDCS Role Mapper Provider
:keywords: helidon, security, idcs
A role mapper to retrieve roles from Oracle IDCS.
==== Setup
[source,xml]
.Maven dependency
----
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-idcs-mapper</artifactId>
</dependency>
----
[source,text]
.Provider class name
----
io.helidon.security.providers.idcs.mapper.IdcsRoleMapperProvider
----
[source,text]
.Provider configuration key
----
idcs-role-mapper
----
==== Example code
https://github.com/oracle/helidon/tree/master/examples/security/idcs-login/[]
[source,yaml]
.Configuration example
----
security:
providers:
- idcs-role-mapper:
multitenant: false
oidc-config:
client-id: "client-id"
client-secret: "client-secret"
identity-uri: "IDCS identity server address"
----
==== Configuration options
The following table shows all configuration options of the provider and their default values
[cols="2,2,5"]
|===
|key |default value |description
|`multitenant` |`true` |Whether to support multi-tenancy with this provider
|`idcs-tenant-handler` |Header `X-USER-IDENTITY-SERVICE-GUID` |Multi-tenant specific `TokenHandler` configuration to retrieve the tenant id
|`idcs-app-name-handler` |Header `X-RESOURCE-SERVICE-INSTANCE-IDENTITY-APPNAME` |Multi-tenant specific `TokenHandler` configuration to retrieve the application name
|`cache-config` |{nbsp} |Configuration of cache of roles for subjects
|`cache-config.cache-enabled` |`true` |Possibility to disable the cache altogether
|`cache-config.max-size` |`100_000` |Maximal number of records in the cache
|`cache-config.cache-timeout-millis` |1 hour |Cache timeout in milliseconds
|`cache-config.cache-evict-delay-millis` |1 minute |How long to wait before starting the first eviction process
|`cache-config.cache-evict-period-millis` |5 minutes |Period of running the eviction process
|`cache-config.parallelism-threshold` |`10_000` |Threshold as used by `ConcurrentHashMap.forEachKey`
|`cache-config.evictor-class` |{nbsp} |Implementation of `BiFunction` that receives key and value, and returns `true` for records that should be removed
from the cache. Eviction mechanism should be fast, as it is called within methods of `ConcurrentHashMap`
|`subject-types` |`USER` |Can use `USER` and/or `SERVICE`
|`default-idcs-subject-type` |`user` |Default subject type to use when requesting roles, can be `user` or `client`
|`oidc-config` |{nbsp} |`OidcConfig` configuration, except `validate-with-jwk` is set to `false`,
and `server-type` is set to `idcs`
|===
==== How does it work?
The provider asks the IDCS server to provide list of roles for the currently
authenticated user.
The result is cached for a certain period of time (see `cache-config` above).

View File

@@ -0,0 +1,120 @@
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2020 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
=== JWT Provider
:description: Helidon Security JWT Provider
:keywords: helidon, security, jwt
JWT token authentication and outbound security provider.
==== Setup
[source,xml]
.Maven dependency
----
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-jwt</artifactId>
</dependency>
----
[source,text]
.Provider class name
----
io.helidon.security.providers.jwt.JwtProvider
----
[source,text]
.Provider configuration key
----
jwt
----
==== Example code
https://github.com/oracle/helidon/tree/master/examples/security/outbound-override[]
[source,yaml]
.Configuration example
----
security:
providers:
- provider:
atn-token:
jwk.resource.resource-path: "verifying-jwk.json"
jwt-audience: "http://my.service"
sign-token:
jwk.resource.resource-path: "signing-jwk.json"
jwt-issuer: "http://my.server/identity"
outbound:
- name: "propagate-token"
hosts: ["*.internal.org"]
- name: "generate-token"
hosts: ["1.partner-service"]
jwk-kid: "partner-1"
jwt-kid: "helidon"
jwt-audience: "http://1.partner-service"
----
==== Configuration options
The following table shows all configuration options of the provider and their default values
[cols="2,2,5"]
|===
|key |default value |description
|`optional` |`false` |If set to `true`, failure to authenticate will return `ABSTAIN` result instead of `FAILURE`. This is
an important distinction when more than one provider is used
|`authenticate` |`true` |Whether to attempt authentication
|`propagate` |`true` |Whether to attempt identity propagation/JWT creation
|`principal-type` |`USER` |Whether we authenticate a user or a service (other option is SERVICE)
|`atn-token` | |A group for configuring authentication of the request
|`atn-token.verify-signature` |`true` |Whether to verify signature in incoming JWT. If disabled, _ANY_ JWT will be accepted
|`atn-token.jwt-audience` |{nbsp} |Expected audience of the JWT. If not defined, any audience is accepted (and we may accept JWT not inteded for us)
|`atn-token.jwk.resource.*` |{nbsp} |Configuration of the JWK to obtain key(s) to validate signatures of inbound token. The JWK should contain public keys. This may be: jwk.resource.path, jwk.resource.resource-path, jwk.resource.url, jwk.resource.content-plain (actual JSON string), jwk.resource.content (base64)
|`atn-token.handler` |`Authorization` header with `bearer ` prefix |A handler configuration for inbound token - e.g. how to extract it
|`atn-token.handler.header` |{nbsp} |Name of a header the token is expected in
|`atn-token.handler.prefix` |{nbsp} |Prefix before the token value (optional)
|`atn-token.handler.regexp` |{nbsp} |Regular expression to obtain the token, first matching group is used (optional)
|`sign-token` |{nbsp} |A group for configuring outbound security
|`sign-token.jwk.resource.*` |{nbsp} |Configuration of the JWK to use when generating tokens (follows same rules as atn-token.jwk above), this JWK must contain private keys when using asymmetric ciphers
|`sign-token.jwt-issuer` |{nbsp} |When we issue a new token, this is the issuer to be placed into it (validated by target service)
|`sign-token.outbound` |{nbsp} |A group for configuring outbound rules (based on transport, host and.or path)
|`sign-token.outbound.*.name` |{nbsp} |A short descriptive name for configured target service(s)
|`sign-token.outbound.*.transports` |any |An array of transports this outbound matches (e.g. https)
|`sign-token.outbound.*.hosts` |any |An array of hosts this outbound matches, may use * as a wild-card (e.g. *.oracle.com)
|`sign-token.outbound.*.paths` |any |An array of paths on the host this outbound matches, may use * as a wild-card (e.g. /some/path/*)
|`sign-token.outbound.*.outbound-token` |`Authorization` header with `bearer ` prefix |Configuration of outbound token handler (same as atn-token.handler)
|`sign-token.outbound.*.outbound-token.format` |{nbsp} |Java text format for generating the value of outbound token header (e.g. "bearer %1$s")
|`sign-token.outbound.*.jwk-kid` |{nbsp} |If this key is defined, we are generating a new token, otherwise we propagate existing. Defines the key id of a key definition in the JWK file to use for signing the outbound token
|`sign-token.outbound.*.jwt-kid` |{nbsp} |A key to use in the generated JWT - this is for the other service to locate the verification key in their JWK
|`sign-token.outbound.*.jwt-audience` |{nbsp} |Audience this key is generated for (e.g. http://www.example.org/api/myService) - validated by the other service
|`sign-token.outbound.*.jwt-not-before-seconds` |`5` |Makes this key valid this amount of seconds into the past. Allows a certain time-skew for the generated token to be valid before current time (e.g. when we expect a certain misalignment of clocks)
|`sign-token.outbound.*.jwt-validity-seconds` |1 day |Token validity in seconds
|===
==== How does it work?
JSON Web Token (JWT) provider has support for authentication and outbound security.
Authentication is based on validating the token (signature, valid before etc.) and on asserting the subject
of the JWT subject claim.
For outbound, we support either token propagation (e.g. the token from request is propagated further) or
support for generating a brand new token based on configuration of this provider.

View File

@@ -0,0 +1,143 @@
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2018, 2020 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
:description: Helidon Security OIDC Provider
:keywords: helidon, security, oidc
[source,text]
.Provider class name
----
io.helidon.security.providers.oidc.OidcProvider
----
[source,text]
.Provider configuration key
----
oidc
----
==== Example code
https://github.com/oracle/helidon/tree/master/examples/security/idcs-login[]
[source,yaml]
.Configuration example
----
security:
providers:
- oidc:
client-id: "client-id-of-this-service"
client-secret: "${CLEAR=client-secret-of-this-service}"
identity-uri: "http://your-tenant.identity-server.com"
frontend-uri: "http://my-service:8080"
audience: "http://my-service"
outbound:
- name: "internal-services"
hosts: ["*.example.org"]
outbound-token:
header: "X-Internal-Auth"
----
==== Configuration options
The following table shows all configuration options of the provider and their default values
[cols="2,2,5"]
|===
|key |default value |description
|`client-id` |{nbsp} |Client ID as generated by identity server
|`client-secret` |{nbsp} |Client secret as generated by identity server
|`identity-uri` |{nbsp} |URI of the identity server, base used to retrieve OIDC metadata
|`frontend-uri` |{nbsp} |Full URI of this service for redirects back from OIDC server
|`issuer` |`issuer` from OIDC metadata |Issuer of token - each JWT is validated to check the issuer
|`audience` | {nbsp} |Audience of a token - each JWT is validated to check the audience
|`proxy-protocol` |`http` |Proxy protocol to use when proxy is used
|`proxy-host` |`null` |Proxy host to use. When defined, triggers usage of proxy for HTTP requests
|`proxy-port` |`80` |Port of the proxy server to use
|`redirect-uri` |`/oidc/redirect` |URI to register web server component on, used by the OIDC server to redirect authorization requests to after a user logs in or approves scopes. Note that usually the redirect URI configured here must be the same one as configured on OIDC server.
|`scope-audience` |empty string |Audience of the scope required by this application. This is prefixed to the scope name when requesting scopes from the identity server.
|`cookie-use` |`true` |Whether to use cookie to store JWT. If used, redirects happen only in case the user is not authenticated or has insufficient scopes
|`cookie-name` |`JSESSIONID` |Name of the cookie
|`cookie-domain` |{nbsp} |Domain the cookie is valid for. Not used by default
|`cookie-path` |`/` |Path the cookie is valid for.
|`cookie-max-age-seconds` |{nsbp} |When using cookie, used to set MaxAge attribute of the cookie, defining how long the cookie is valid.
|`cookie-http-only` |`true` |When using cookie, if set to true, the HttpOnly attribute will be configured.
|`cookie-secure` |`false` |When using cookie, if set to true, the Secure attribute will be configured.
|`cookie-same-site` |`Lax` |When using cookie, used to set the SameSite cookie value. Can be "Strict" or "Lax". Setting this to "Strict" will result in infinite redirects when calling OIDC on a different host.
|`query-param-use` |`false` |Whether to expect JWT in a query parameter
|`query-param-name` |`accessToken` |Name of a query parameter that contains the JWT token when parameter is used.
|`header-use` |`false` |Whether to expect JWT in a header field.
|`header-token` |`Authorization` header with prefix `bearer` |A TokenHandler configuration to process header containing a JWT
|`oidc-metadata-well-known` |`true` |If set to true, metadata will be loaded from default (well known) location, unless it is explicitly defined using oidc-metadata-resource. If set to false, it would not be loaded even if oidc-metadata-resource is not defined. In such a case all URIs must be explicitly defined (e.g. token-endpoint-uri).
|`oidc-metadata.resource` |`identity-uri/.well-known/openid-configuration` |Resource configuration for OIDC Metadata containing endpoints to various identity services, as well as information about the identity server. See Resource.create(io.helidon.config.Config)
|`token-endpoint-uri` |`token_endpoint` in OIDC metadata, or `identity-url/oauth2/v1/token` if not available |URI of a token endpoint used to obtain a JWT based on the authentication code.
|`authorization-endpoint-uri` |"authorization_endpoint" in OIDC metadata, or `identity-uri/oauth2/v1/authorize` if not available |URI of an authorization endpoint used to redirect users to for logging-in.
|`validate-with-jwk` |`true` |When true - validate against jwk defined by "sign-jwk", when false validate JWT through OIDC Server endpoint "validation-endpoint-uri"
|`sign-jwk.resource` |"jwks-uri" in OIDC metadata, or `identity-uri/admin/v1/SigningCert/jwk` if not available, only needed when jwt validation is done by us |A resource pointing to JWK with public keys of signing certificates used to validate JWT. See Resource.create(io.helidon.config.Config)
|`introspect-endpoint-uri` |"introspection_endpoint" in OIDC metadata, or `identity-uri/oauth2/v1/introspect` |When validate-with-jwk is set to "false", this is the endpoint used
|`base-scopes` |`openid` |Configure scopes to be requested by default. If the scope has a qualifier, it must be included here
|`redirect` |`true` |Whether to redirect to identity server when authentication failed.
|`realm` |`helidon` |Realm returned in HTTP response if redirect is not enabled or possible.
|`redirect-attempt-param` |`h_ra` |Query parameter holding the number of times we redirected to an identity server. Customizable to prevent conflicts with application parameters
|`max-redirects` |`5` |Maximal number of times we can redirect to an identity server. When the number is reached, no further redirects happen and the request finishes with an error (status 401)
|`server-type` |{nbsp} |Type of identity server. Currently supported is idcs or not configured (for default).
|`propagate` |{nbsp} |Whether to propagate the token we have. Defaults to `false` unless an outbound configuration is defined
|`outbound` |{nbsp} |A list of outbound configurations
|`outbound.*.name` |{nbsp} |Required name of outbound configuration
|`outbound.*.transports` |any transport |An array of transports this outbound configuration should be used for
|`outbound.*.hosts` |any host |An array of hosts this outbound configuration should be used for, can be a regular expression
|`outbound.*.paths` |any path |An array of paths this outbound configuration should be used for (such as `/greet`), can be a regular expression
|`outbound.*.methods` |any method |An array of HTTP methods this outbound configuration should be used for
|`outbound.*.outbound-token` |`Authorization` header with `bearer` prefix |Configuration of outbound header used to propagate
|`outbound.*.outbound-token.header` |{nbsp} |Name of the header used to propagate the token
|`outbound.*.outbound-token.prefix` |{nbsp} |Prefix for the header value, such as `"bearer"` (only one of `prefix`, `regexp` and `format` should be defined, `regexp` wins over `prefix`, `format` wins over `regexp`)
|`outbound.*.outbound-token.format` |{nbsp} |String format with a single parameter to create the header value, such as `"bearer %1s"`
|`outbound.*.outbound-token.regexp` |{nbsp} |Regular expression to create the header value, such as `"bearer (.*)"`
|===
==== How does it work?
At Helidon startup, if OIDC provider is configured, the following will happen:
1. `client-id`, `client-secret`, and `identityUri` are validated - these must provide values
2. Unless all resources are configured as local resources, the provider attempts
to contact the `oidc-metadata.resource` endpoint to retrieve all endpoints
At runtime, depending on configuration...
If a request comes without a token or with insufficient scopes:
1. If `redirect` is set to `true` (default), request is redirected to the authorization
endpoint of the identity server. If set to false, `401` is returned
2. User authenticates against the identity server
3. The identity server redirects back to Helidon service with a code
4. Helidon service contacts the identity server's token endpoint, to exchange the code
for a JWT
5. The JWT is stored in a cookie (if cookie support is enabled, which it is by default)
6. Helidon service redirects to original endpoint (on itself)
Helidon obtains a token from request (from cookie, header, or query parameter):
1. Token is parsed as a singed JWT
2. We validate the JWT signature either against local JWK or against the identity server's
introspection endpoint depending on configuration
3. We validate the issuer and audience of the token if it matches the configured values
4. A subject is created from the JWT, including scopes from the token
5. We validate that we have sufficient scopes to proceed, and return `403` if not
6. Handling is returned to security to process other security providers

View File

@@ -0,0 +1,73 @@
///////////////////////////////////////////////////////////////////////////////
Copyright (c) 2020 Oracle and/or its affiliates.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
///////////////////////////////////////////////////////////////////////////////
=== Provider
:description: Helidon Security ... Provider
:keywords: helidon, security
Short description
==== Setup
[source,xml]
.Maven dependency
----
<dependency>
<groupId>io.helidon.security.providers</groupId>
<artifactId>helidon-security-providers-</artifactId>
</dependency>
----
[source,text]
.Provider class name
----
io.helidon.security.providers.
----
[source,text]
.Provider configuration key
----
provider-config-key
----
==== Example code
https://github.com/oracle/helidon/tree/master/examples/security/[]
[source,yaml]
.Configuration example
----
security:
providers:
- provider:
key: "value"
----
==== Configuration options
The following table shows all configuration options of the provider and their default values
[cols="2,2,5"]
|===
|key |default value |description
|`key` |{nbsp} |Description
|===
==== How does it work?
Detailed description