<objectType>
...
<credentials>
<password>
...
<caching>
<cachingStrategy>passive</cachingStrategy>
</caching>
...
</password>
</credentials>
...
</objectType>
Password Caching in 4.9.1
This is a historic design document. It was transformed into a section in the feature description. |
Overview
We introduced production-ready implementation of shadow caching in 4.9, even with some limitations. One of them is missing password caching. It is being implemented for 4.9.1 (and, therefore, 4.10 as well) now. This document provides main ideas behind the implementation.
TL;DR
See the limitations. |
Use Cases
-
We have a limited feature called prohibited values. While providing it, midPoint needs to be able to check that, e.g., a password being set up (at some place) for given user is not the same as the password on specific (or any) user’s account.
This feature is there since 3.7.
Look for
prohibitedValues
in tests files; especially relevant test istest512JackInitializeAccountMaverickAlligator
inAbstractPasswordTest
. -
The cached value is used to determine if there is a password or not, so that outbound weak mappings are executed correctly.
This is a new feature (since 4.9.1).
See e.g.
test312ChangeUserPassword
inAbstractPasswordTest
. -
The cached value is really used. For example, we may have a database table as a source resource, providing users' initial passwords. We may want to cache these, so that they won’t be repeatedly requested.
State Before 4.9.1
We stored the passwords only when writing, i.e., when midPoint was creating an account or updating its password on the resource. This was somehow sufficient to cover the use case #1 (prohibited values), although only when midPoint was the sole source of password changes. (That means, if the password was changed on the resource, midPoint didn’t get that information.)
Also, midPoint stored password always in the hashed form.
Legacy Configuration
The pre-4.9.1 way of configuring password caching still works, although it’s deprecated now:
To avoid surprising behavior changes, there are the following limitations when using this legacy configuration:
-
Storage is always in the hashed form, to avoid unintended password exposure (even in the encrypted form).
-
Passwords are not updated when objects are fetched from the resource.
Current Design
Now we store the passwords both when writing as well as when reading. There are the following possible states of the cached password value:
-
no value,
-
no value, but the
incomplete
flag beingtrue
, -
encrypted value,
-
hashed value.
Storing shadow passwords when reading is complicated by the fact that resources usually do not provide actual values of the passwords when shadows are being read.
Sometimes, they return an indication whether the password does exist or not.
Sometimes, they return a hashed value (e.g., like {SSHA}rxNYgQODi95h2bsjYXuBqvYz+I1gjgMkF9f0tA== for LDAP).
But most of the time they do not return anything at all.
|
Behavior When Writing
If there is a value that is going to be sent to the resource, store it as encrypted or hashed, depending on the appropriate security policy.
See:
-
ShadowObjectComputer.preparePasswordForStorage(..)
(forADD
operations on shadows) -
ShadowDeltaComputerRelative.addPasswordValueDelta(..)
(forMODIFY
operations on shadows)
Behavior When Reading
-
If a clear text is returned from the resource, store it in the encrypted or hashed form.
This serves all use cases, although there is a catch: if the value obtained from the resource is already hashed (like in LDAP), storing it in the shadow breaks down the "prohibited values" feature, because the value hashed by LDAP cannot be used for any comparisons. It breaks down any other uses, e.g., inbound mappings, checking for application of strong outbound mappings, etc. A reasonable advice is to avoid
readable
setting forpasswordReadStrategy
for LDAP resources - generally, any resources that return a value that is mangled in some way. Eitherincomplete
orunreadable
(the default) is OK. See the limitations. -
If an
incomplete
oftrue
flag is returned, and a hashed or encrypted value is present, keep that value.The stored value will no longer reflect the current value on the resource (which could have been changed outside midPoint) but this behavior is no worse than it was before 4.9.1.
The most problematic scenario is:
-
incomplete
read, -
storing encrypted passwords,
-
useCachedOrFresh
strategy, -
inbound mappings present,
-
we sometimes write something, and sometimes there is a change directly on the resource.
The output of the inbound mappings would be based only on password values written by midPoint, never by those from the resource. The values from the resource are not available anyway. What is lost, though, is the information that we don’t know what’s on the resource.
The recommendation would be to not use inbounds (or strong outbounds) in these cases. See the limitations.
-
-
If we expected the password, and got none, the cached value is removed.
-
If we didn’t expect the password (and got none), the state is not changed.
How do we know whether the password is expected? Currently, we assume that if the password is readable (in full or incomplete form), it will be always retrieved - regardless of whether it is returned by default, and regardless of the configured fetch strategy. I believe that the current behavior is faulty, though. This can break password caching if the capability declares password as readable, but the password is actually not readable.
See:
-
ShadowObjectComputer.preparePasswordForStorage(..)
(for newly discovered objects) -
ShadowDeltaComputerAbsolute.updateCachedCredentials(..)
(for reading objects that have existing shadows)
Management of Caching Related Configuration
-
Either via refreshing the cache by reading the data from the resource.
-
Or using Shadow Refresh task.
Limitations
-
Use case #1 (prohibited values) cannot be used with readable passwords for resources that return passwords in changed form (typically, as hash for LDAP). In such cases, the connector must be configured to either provide no information about the password, or to provide existence information only. Temporarily, it is possible to use the legacy (deprecated) style of the password caching.
-
When using the following configuration: (1)
incomplete
password reading mode, (2) caching turned on withencryption
for the storage, (3)useCachedOrFresh
cache mode, (4) password change both from midPoint and from the resource, and (5) inbound mappings and/or strong outbound mappings, then the results of inbound mappings and/or application of strong outbound mappings would be unpredictable. (Note that prohibited values feature would not know about the changed password, but that’s acceptable, as there’s no way of transferring the value from the resource to midPoint.)
(Complete list of limitations is in the feature description.)