Skip to content

Commit

Permalink
GEODE-9933: documentation for authorization expiry (apache#7248)
Browse files Browse the repository at this point in the history
* GEODE-9933: documentation for authorization expiry

Co-authored-by: Dave Barnes <[email protected]>
  • Loading branch information
jmelchio and davebarnes97 authored Jan 19, 2022
1 parent fef9a4f commit 8f7193c
Show file tree
Hide file tree
Showing 7 changed files with 162 additions and 27 deletions.
3 changes: 3 additions & 0 deletions geode-book/master_middleman/source/subnavs/geode-subnav.erb
Original file line number Diff line number Diff line change
Expand Up @@ -619,6 +619,9 @@ limitations under the License.
<li>
<a href="/docs/guide/<%=vars.product_version_nodot%>/managing/security/authentication_examples.html">Authentication Example</a>
</li>
<li>
<a href="/docs/guide/<%=vars.product_version_nodot%>/managing/security/implementing_authentication_expiry.html">Implementing Authentication Expiry</a>
</li>
</ul>
</li>
<li class="has_submenu">
Expand Down
23 changes: 22 additions & 1 deletion geode-docs/managing/security/authentication_examples.html.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,8 @@ then the password provided within the `credentials` parameter
is compared to the data structure's known password for that user name.
Upon a match, the authentication is successful.

## <a id="username_password_authentication_example"></a>Username, Password Authentication Example

``` pre
public Object authenticate(final Properties credentials)
throws AuthenticationFailedException {
Expand All @@ -58,7 +60,7 @@ public Object authenticate(final Properties credentials)
"SampleSecurityManager: wrong username/password");
}

if (user != null
if (user != null
&& !userObj.password.equals(password)
&& !"".equals(user)) {
throw new AuthenticationFailedException(
Expand All @@ -68,3 +70,22 @@ public Object authenticate(final Properties credentials)
return user;
}
```

## <a id="token_with_expiry_authentication_example"></a>Token with Expiry Authentication Example

``` pre
public Object authenticate(final Properties credentials)
throws AuthenticationFailedException, AuthenticationExpiredException {
String encodedToken = credentials.getProperty(ResourceConstants.TOKEN);

if (this.tokenHasExpired(encodedToken)) {
throw new AuthenticationExpiredException("SampleSecurityManager: token has expired");
}
User user = this.encodedTokenToUser.get(encodedToken);
if (user == null) {
throw new AuthenticationFailedException("SampleSecurityManager: unable to get user from token");
}

return user;
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,8 @@ cluster such as peers, clients, and those connecting to a JMX manager.
The example demonstrates the basics of an implementation of the
`SecurityManager.authenticate` method.

- **[Implementing Authentication Expiry](implementing_authentication_expiry.html)**

Client credentials can be given a limited life time to enhance the security of the cluster.


18 changes: 18 additions & 0 deletions geode-docs/managing/security/authorization_example.html.md.erb
Original file line number Diff line number Diff line change
Expand Up @@ -74,3 +74,21 @@ public boolean authorize(Method method, Object target) {
return method.isAnnotationPresent(Authorized.class);
}
```

## <a id="token_with_expiry_authorization_example"></a>Token with Expiry Authorization Example

This example assumes that the system is using an expirable token that needs to be checked for expiry
before resource permissions are verified.

``` pre
public boolean authorize(final Object tokenUser, final ResourcePermission context)
throws AuthenticationExpiredException {
// Check expiry
if (this.tokenHasExpired(tokenUser)) {
throw new AuthenticationExpiredException("SampleSecurityManager: token has expired");
}

// Check permissions
return this.tokenHasRequiredPermissions(tokenUser, context);
}
```
Original file line number Diff line number Diff line change
Expand Up @@ -29,9 +29,13 @@ When a component initiates a connection to the cluster, the `SecurityManager.aut
The component provides its credentials in the form of properties as a parameter to the `authenticate` method.

The credentials parameter is generated by the `security-client-auth-init` class's `getCredentials()` call,
for example a token, a certificate, or a user/password combination.
The `authenticate` method is expected to either return an object
representing a principal or throw an `AuthenticationFailedException`.
for example a token, a certificate, or a user/password combination. The `authenticate` method is
expected to either return an object representing a principal or throw an `AuthenticationFailedException`
or `AuthenticationExpiredException`. The principal object is what is passed on to the `authorize` method
which is discussed in detail in the [authorization](authorization_overview.html) section.

In case of an `AuthenticationExpiredException` the <%=vars.product_name%> client code will make one automatic attempt
to re-connect to the member that sent the exception.

A well-designed `authenticate` method will have a set of known credentials, such as user and password pairs, that can be
compared to the credentials presented or will have a way of obtaining those credentials.
Expand All @@ -40,7 +44,8 @@ compared to the credentials presented or will have a way of obtaining those cred

In order to connect with a locator that performs authentication,
a server must set its credentials, a username and password specified as the two properties
`security-username` and `security-password`.
`security-username` and `security-password`. In case of systems that use tokens for authentication
the property `security-token` should be provided.

Choose one of two ways to set the server credentials:

Expand All @@ -49,16 +54,23 @@ Choose one of two ways to set the server credentials:

### <a id="authentication-setserverprops"></a>Add Settings to the Server Properties File

Set `security-username` and `security-password` in the server's
Set `security-username` and `security-password` or `security-token` in the server's
`gfsecurity.properties` file, which is read upon server startup.
For example:

``` pre
security-username=admin
security-password=xyz1234
```
The username and password are stored in cleartext, so the
`gfsecurity.properties` file must be protected by restricting access with

Or:

```pre
security-token=abcdxyz
```

The username, password, and tokens are generally base64 encoded strings which are stored in
cleartext, so the `gfsecurity.properties` file must be protected by restricting access with
file system permissions.

### <a id="authentication-implementserverinterface"></a>Implement the AuthInitialize Interface for the Server
Expand All @@ -83,17 +95,17 @@ example
```

Implement the `getCredentials` method within the `AuthInitialize` interface to acquire values for
the `security-username` and `security-password` properties in whatever way you wish. For example,
it might look up values in a database or another external resource.
the `security-token` property or the `security-username` and `security-password` properties in whatever way you
wish. For example, it might look up values in a database or another external resource.

Gateway senders and receivers communicate as components of their respective server members. Therefore, the
credentials of the server become those of the gateway sender or receiver.

## <a id="authentication-client-set-creds"></a>How a Client Cache Sets its Credentials

In order to connect with a locator or a server that performs authentication,
a client must set its credentials. The credentials parameter is generated by the `security-client-auth-init` class's `getCredentials()` call,
for example a token, a certificate, or a user/password combination.
a client must set its credentials. The credentials parameter is generated by the `security-client-auth-init`
class's `getCredentials()` call, for example a token, a certificate, or a user/password combination.

You must perform two actions to set to set the client credentials:

Expand Down Expand Up @@ -122,16 +134,22 @@ that implements the `AuthInitialize` interface as in the example
```

Implement the `getCredentials()` method of the `AuthInitialize` interface for the client to acquire values for
the `security-username` and `security-password` properties in whatever way
the `security-token` property or the `security-username` and `security-password` properties in whatever way
wish. For example, it might look up values in a database or another external resource,
or it might prompt for values.

When implementing the `getCredentials()` method for a token based system keep in mind that the token
provider may return an existing token for a user and that this token may expire sooner than expected.
Make sure to understand the implications of this and consider building in a check for imminent expiry
in the `getCredentials()` implementation so that a newly fetched but soon to be expired token does
not cause undesired exceptions when used for operations.


### <a id="authentication-provideclientcredaccess"></a>Provide Secure Access to Client Credentials

Set the `security-username` and `security-password` properties for the client in a way that can be
accessed by the `getCredentials` implementation in `AuthInitialize`. This can be done via the APIs,
properties file or other external sources:
Set the `security-token` property or the `security-username` and `security-password` properties for the client in
a way that can be accessed by the `getCredentials` implementation in `AuthInitialize`. This can be
done via the APIs, properties file or other external sources:

``` pre
Properties properties = new Properties();
Expand All @@ -151,11 +169,11 @@ to the `authenticate` method in the keys of `security-username` and `security-pa

Pulse prompts for the username and password upon start up.

Due to the stateless nature of the REST API,
a web application or other component that speaks to a server or locator
via the REST API goes through authentication on each request.
The header of the request needs to include attributes that define values for
`security-username` and `security-password`.
Due to the stateless nature of the REST API, a web application or other component that speaks to a
server or locator via the REST API goes through authentication on each request.
The header of the request needs to include attributes that define values for `security-username` and
`security-password` or in case of token based security the appropriate header associated with the
scheme such as `Authorization: Bearer [encoded token-string]` for `OAuth`

## <a id="authentication-implement-security-mgr"></a>Implement SecurityManager Interface

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
---
title: Implementing Authentication Expiry
---

<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->

Authentication expiry makes it possible for cluster administrators to limit the life span of client
and peer connections within the cluster. The use of expirable credentials is most common when used in
combination with token based authentication and authorization.

Client connections are notified of expiry through the throwing of an `AuthenticationExpiredException`
which is thrown in the implementations of `SecurityManager.authenticate` or `SecurityManager.authorize`.

Clients will do one automatic attempt to reconnect. Upon receiving a second `AuthenticationExpiredException`
the exception will be propagated up the chain for the user to handle. There are some differences in
behavior between older and newer clients.

**Support for Automatic Reconnect**

Client version | single user ops | multi user ops | single user CQ/RI | multi user CQ/RI
--- | :---: | :---: | :---: | :---:
Geode 1.15 and later | Y | Y | Y | N
older than Geode 1.15 | Y | Y | N | N

``` pre
Y = supported
N = not supported
```

Clients older than version 1.15 will also be able to do an automatic reconnect unless the connection
is one of the following types where the exception will always be propagated up the chain:

* multi-user client mode
* event-dispatching (CQ and registered interest)

## <id="authentication_expiry_considerations"></a>Authentication Expiry Considerations

The common cycle for authentication and authorization is the following:

```pre
AuthInitialize.getCredentials(...) -> SecurityManager.authenticate(...) -> SecurityManager.authorize(...)
```

Where `AuthInitialize.getCredentials()` provides the `security properties` for `SecurityManager.authenticate()`
which in turn provides the `principal object` for `SecurityManager.authorize()`. It's important to
understand that some time will pass between the `AuthInitialize.getCredentials()` call and the
`SecurityManager.authorize()` call. The specific amount of time depends on the implementation and
runtime environment details.

In case of the use of an external token provider we assume that this token provider will be asked for
a token in the `AuthInitialize.getCredentials()` call. A token provider can return existing tokens for
a given user so it is recommended that implementers of the `AuthInitialize` and `SecurityManager`
interfaces take imminent timeout and token refresh in consideration to avoid receiving multiple
unintended `AuthenticationExpiredException`s in a row and having to deal with the propagation of these
exceptions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,13 @@ the `SecurityManager.authorize` method is invoked.
It is passed the principal of the operation's requester
and a `ResourcePermission`, which describes the operation requested.

The implementation of the `SecurityManager.authorize` method
makes a decision as to whether or not the principal will be granted permission
to carry out the operation.
It returns a boolean in which a return value of `true` permits
the operation,
and a return value of `false` prevents the operation.
The implementation of the `SecurityManager.authorize` method makes a decision as to whether
the principal will be granted permission to carry out the operation. It returns a boolean in which
a return value of `true` permits the operation, and a return value of `false` prevents the operation.
The operation can also throw an `AuthenticationExpiredException`.

In case of an `AuthenticationExpiredException` the <%=vars.product_name%> client code will make one automatic attempt
to re-connect to the member that sent the exception.

A well-designed `authorize` method will have or will have a way of obtaining
a mapping of principals to the operations (in the form of resource permissions)
Expand Down

0 comments on commit 8f7193c

Please sign in to comment.