Bridging the gap between Users in the system and Users of the system

Overview

The purpose of this effort is to determine the preferred path forward for bridging the gap between users in the system (e.g. patrons, etc.) and users of the system (those who log in and use Folio).

Background

When Eureka introduced Keycloak, it was clear that some user information needs to reside in Keycloak. The term AuthUsers was used to refer to user records in Keycloak, composed primarily of data required for authentication and authorization, but also of some ancillary data to identify the user and facilitate administration / user mgmt. The prevailing thought was that we don’t need AuthUser records for all users in the system. In fact, the vast majority of users in Folio represent patrons or other people who are users in the system, but not users of the system. For clarity, users in the system but not of the system are referred to as “Person” records.

Problem Statement

Problems arise when certain operations are performed on person records. Setting a password, assigning roles, etc. all require an AuthUser record to succeed. The purpose of this investigation is to answer the question: How do we bridge the gap between Persons and AuthUsers?

Scope

In Scope

  • All operations which create or modify user records in Folio

    • Creation/mgmt of users in the Folio UI

    • Creation/mgmt of users via Folio APIs

    • User import via mod-users-import

    • User migration APIs for migrating from the legacy platform to the Eureka platform

    • Assignment of roles/capabilities to users

    • Credential management (Setting a user’s password)

    • Support / System / Tenant users

Out of Scope

  • Changes to the legacy platform are out of scope since this problem is specific to the Eureka platform

Deliverables

There are two main approaches to consider. Key evaluation metrics include:

  • Required effort

  • Solution completeness

  • Performance

  • Cost

  • Security

  • Solution complexity

  • Architectural fit

  • User experience

  • Hosting/administrative impact

Option 1 - Promotion of users in the system to users of the system

Introduce a mechanism for promoting a user in the system to a user of the system. Only users of the system will have AuthUser records. Users can be created via standard mechanisms familiar to those using the legacy platform (mod-users, mod-users-import, etc.). These users will not initially have AuthUser records, but such records will be created as needed. There are two flows here:

Implicit

  • When performing actions which require an AuthUser, check if one exists, and create one if needed. Examples of this include setting a user’s password, or assigning roles/capabilities to the user.

  • Whether or not this “just in time” AuthUser creation happens automatically behind the scenes, or if confirmation is required on the part of the user is an implementation detail, and could even be configurable behavior.

Explicit

  • Allow administrators to explicitly promote a user, resulting in the creation of an AuthUser record. This is mostly a UX concern given that a use could be explicitly promoted by performing an operation which triggers the implicit creation of an AuthUser record (e.g. setting the user’s password).

  • It may be desirable to add an action menu item, button, etc. to ui-users allowing for explicit promotion.

NOTE: This can initially be implemented as a one-way operation, and later incorporate a way to demote a user (removing their AuthUser record).

NOTE: Presently, ui-users has been modified for Eureka to conditionally call the mod-users-keycloak APIs when creating/modifying users. This means that all users created via the users UI will have AuthUser records. The expectation is that if we choose to implement this solution, those changes will be backed out and ui-users will interact with mod-users. The only potential exception being explicit promotion (see above).

Implementation

  • Create a new endpoint in mod-users-keycloak which performs the check to see if an AuthUser record exists for the specified user, and optionally create one if needed.

  • Rollback/revert the changes in ui-users which conditionally call mod-users-keycloak

  • Adjustments to mod-login-keycloak (POST /authn/credentials) to call the new endpoint described above prior to attempting to set credentials

  • Adjustments to mod-roles-keycloak (POST /roles/users) to call the new endpoint described above prior to attempting to create role/user relationships.

  • Adjustments to mod-roles-keycloak (POST /users/capabilities) to call the new endpoint described above prior to attempting to create/modify user/capability assignments.

  • Adjustments to mod-roles-keycloak (POST /users/capability-sets) to call the new endpoint described above prior to attempting to create/modify user/capability-set assignments.

NOTE: If we want to do something like display a confirmation dialog to the user prior to JIT user creation, we would need to also make adjustments in ui-developer (passwd), ui-authorization-roles/ui-users (role/user, capability/user, capability-set/user assignments)

Pros

  • Existing code in mod-users, mod-users-import, etc. does not need to change.

  • AuthUsers only exist for users of the system, keeping the number of users in Keycloak lower, and more manageable.

  • From the administrator’s perspective, the impact on their processes should be minimal. Depending on details of the implementation, there could be no impact at all.

Cons

  • There are a bunch of places where additional business logic is required to check for AuthUser presence and just-in-time creation. However, much of the business logic can be centralized by creating a new API endpoint in mod-users-keycloak and calling it as needed from other modules.

Option 2 - Users in the system == Users of the system

Create AuthUser records for all users in Folio. This means that users need to always be created via mod-users-keycloak, either directly or indirectly.

  • ui-users already uses mod-users-keycloak when the users-keycloak interface is enabled for the current tenant

  • mod-user-import would need to be adjusted to also (conditionally) call mod-users-keycloak instead of mod-users behind the scenes. As far as determining in the module whether we should call mod-users or mod-users-keycloak, we have several options, none of which are fantastic…

    • We could add an environment variable which controls this.  Probably the easiest thing to do, but maybe not ideal.

    • We could try to determine if the interface is available, but that requires calling OKAPI on the legacy platform and the mgr-tenant-entitlements on the new platform... so how do we determine which endpoint to call?  

      • Make this an env. variable configuration?  I feel like this gets complicated fast given that the OKAPI and MTE responses are completely different, etc.

      • Try calling OKAPI, if the call fails, try calling MTE.  Maybe only need to do this once at startup to determine which needs to be called, then perform the interface availability check as needed (probably with some caching).  I think the circuit breaker pattern is overkill here... it's unlikely that you're going to change from OKAPI -> MTE or vice versa once the system is running (aside from during migration of old -> new platform)

  • There’s always the risk that the mod-users API will be invoked directly. There isn’t much we can do to prevent that aside from documenting the implications of doing so.

    • There are ways to address this, but none of them are particularly great:

      • Add logic in mod-users to call mod-users-keycloak to ensure an AuthUser record is created. Unfortunately this will cause a circular dependency between mod-users and mod-users-keycloak. There are ways to get around this (e.g. incorporate Kafka) but the further we chase this the more complex the solution becomes.

      • Merge mod-users and mod-users-keycloak into a single module. On one hand this simplifies things. On the other hand, if we want this combined users module to work on both the legacy and Eureka platform, it would probably mean supporting multiple code paths. Regardless of the platform you’re using, there would likely be a non-trivial amount of unreachable code.

Implementation

  • mod-user-import would need to be adjusted to also (conditionally) call mod-users-keycloak, e.g. based on one or more environment variables.

Pros

  • There’s no need to check if AuthUser records exist or for just-in-time user creation. Those records should always be present

Cons

  • There isn’t a simple, reliable way to prevent the direct use of mod-users for user creation

  • Could potentially become complex.

  • AuthUser records are created when not needed

  • Potential performance impact with having a very large number of users in Keycloak

Comparison

 

Option 1

Option 2

 

Option 1

Option 2

Required effort

Moderate - The work is not difficult or complex, but changes are required in several places.

Easy - Moderate (it depends)

In it’s simplest form very little effort is required. A more complete solution would require additional effort.

Solution Completeness

Complete, aside from “demotion” of users, but this doesn’t preclude that from being added later.

Complete aside from preventing direct use of mod-users for user creation/mgmt.

Performance

Some operations which require checking for AuthUser records and JIT creation may be slower, but these are administrative operations as opposed to librarian operations (check-in/check-out/acquisitions/cataloging/etc.)

  • User creation may be very slightly slower as records need to be created in both Keycloak and Folio, requiring additional API calls

  • There is no evidence to back this up, but it’s possible some Keycloak operations could be slower when there are a very large number of AuthUser records.

Cost

Same

Same

Security

More secure since AuthUser records exist only for users of the system.

Less secure since all users have AuthUser records. This means patrons and other users in the system are closer to being able to authenticate when really there is no need for that.

Solution complexity

Moderate

Simple - Complex (it depends)

In it’s basic form this solution is very simple. A more complete solution would require significantly more complexity.

Architectural fit

Good

Less ideal since it’s considered unattractive to have AuthUser records for ALL users

UX

Dependent on implementation details

Unchanged

Hosting/admin impact

None

Potentially may need to specify additional environment variables when starting various modules.

Workaround(s)

One possible workaround which can be leveraged in the short term is to educate users on how different classes or types of users should be created. For instance:

  • Use mod-user-import for creating many users who will not be authenticating and using the system

  • Use mod-users-keycoak for creating users who will be using the system (staff, admins, etc.)

  • etc.

Another workaround is to call the migration API provided by mod-users-keycloak to ensure AuthUser records are created. This would likely require some minor adjustments to this endpoint to be effective.

Conclusion

The decision was made to proceed with Option #1.

Spike Status: Complete

Open issues & Considerations