Skip to content

Conversation

pflynn-virtru
Copy link
Member

@pflynn-virtru pflynn-virtru commented May 12, 2025

Introduce Support for Multiple Users and Clients in Keycloak Setup

Overview

This PR enhances the Keycloak setup functionality by introducing support for creating multiple users and clients dynamically. It modifies the SetupCustomKeycloak function and updates the Keycloak data structure to allow specifying a count for users and clients, ensuring scalability and flexibility in managing Keycloak resources.

- user
  copies: 100
- client
  copies: 10

results in an additional 100 users with -000 -- -999 prepended

Key Changes

  1. Updated RealmToCreate Structure:

    • Replaced Users field type from []gocloak.User to []User.
    • Introduced a new User struct that extends gocloak.User with an additional Count field.
    • Added a Count field to the Client struct to allow creating multiple clients.
  2. Dynamic User and Client Creation:

    • Modified the SetupCustomKeycloak function:
      • Added logic to iterate and create multiple users and clients based on the provided Count value.
      • Implemented padding for generated user IDs and usernames, as well as client IDs, to ensure proper formatting.
  3. Mathematical Calculations:

    • Introduced math package for calculating the number of digits required for padding user and client identifiers.
  4. Configuration Changes:

    • Updated keycloak_data.yaml to include the new count field for clients and users under realms.
  5. Code Annotations:

    • Added a //nolint:nestif comment to suppress linting errors for nested if-statements in the client creation logic.

Code Example

  • Dynamic Client Creation:

    numDigits := int(math.Log10(float64(customClient.Count-1))) + 1
    padFormat := fmt.Sprintf("%%s-%%%dd", numDigits)
    for i := 0; i < customClient.Count; i++ {
        customClient.Client.ClientID = gocloak.StringP(fmt.Sprintf(padFormat, customClient.Client.ClientID, i))
        _, err = createClient(ctx, client, token, &kcConnectParams, customClient.Client, realmRoles, clientRoleMap)
        if err != nil {
            return err
        }
    }
  • Dynamic User Creation:

    numDigits := int(math.Log10(float64(customUser.Count-1))) + 1
    padFormat := fmt.Sprintf("%%s-%%%dd", numDigits)
    for i := 0; i < customUser.Count; i++ {
        customUser.User.ID = gocloak.StringP(fmt.Sprintf(padFormat, customUser.User.ID, i))
        customUser.User.Username = gocloak.StringP(fmt.Sprintf(padFormat, customUser.User.Username, i))
        _, err = createUser(ctx, client, token, &kcConnectParams, customUser.User)
        if err != nil {
            return err
        }
    }

Benefits

  • Scalability: Enables creating multiple users and clients programmatically, reducing manual configuration overhead.
  • Flexibility: Simplifies Keycloak setup for scenarios requiring a large number of users or clients.
  • Improved Configuration: Introduces a more streamlined and dynamic approach to managing Keycloak data through YAML configuration.

Files Modified

  1. lib/fixtures/keycloak.go:
    • Updated structures and logic for user and client creation.
  2. service/cmd/keycloak_data.yaml:
    • Added count fields to sample configuration.

Testing

  • Verified the creation of multiple users and clients with correct padding using updated YAML configuration.
  • Confirmed backward compatibility for existing setups without the count field.

Notes

  • The count field defaults to 1 if not provided.
  • Linting exceptions were added where necessary to maintain readability and functionality.

Checklist

  • I have added or updated unit tests
  • I have added or updated integration tests (if appropriate)
  • I have added or updated documentation

Testing Instructions

Copy link
Contributor

Benchmark results, click to expand

Benchmark Results:

Metric Value
Approved Decision Requests 5000
Denied Decision Requests 0
Total Time 465.300044ms

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 363.650071ms
Throughput 274.99 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 1m16.474132939s
Average Latency 761.585982ms
Throughput 65.38 requests/second

NANOTDF Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 4942
Failed Requests 58
Concurrent Requests 50
Total Time 1m7.827147235s
Average Latency 674.308843ms
Throughput 72.86 requests/second

Error Summary:

Error Message Occurrences
ReadNanoTDF error: getNanoRewrapKey: rewrapError: internal: internal error
rpc error: code = Internal desc = could not perform access
58 occurrences

Standard Benchmark Metrics Skipped or Failed

@pflynn-virtru pflynn-virtru changed the title Keycloak duplicate users and clients feat: cmd for bulk keycloak provisioning May 12, 2025
Copy link
Contributor

Benchmark results, click to expand

Benchmark Results:

Metric Value
Error rpc error: code = Internal desc = resource retrieval failed
Total Time 62.16917ms

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 0
Failed Decrypts 100
Total Time 167.156086ms
Throughput 0.00 requests/second

Error Summary

Error Message Occurrences
splitKey.unable to reconstruct split key: map[{http://localhost:8080 }:kao unwrap failed for split {http://localhost:8080 }: internal: internal error
rpc error: code = Internal desc = could not perform access]
kao unwrap failed for split {http://localhost:8080 }: internal: internal error
rpc error: code = Internal desc = could not perform access 100 occurrences

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 0
Failed Requests 5000
Concurrent Requests 50
Total Time 31.336268095s
Throughput 0.00 requests/second

Error Summary:

Error Message Occurrences
read error: reader.WriteTo failed: splitKey.unable to reconstruct split key: map[{http://localhost:8080 }:kao unwrap failed for split {http://localhost:8080 }: internal: internal error
rpc error: code = Internal desc = could not perform access]
kao unwrap failed for split {http://localhost:8080 }: internal: internal error
rpc error: code = Internal desc = could not perform access
5000 occurrences

NANOTDF Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 0
Failed Requests 5000
Concurrent Requests 50
Total Time 24.145925149s
Throughput 0.00 requests/second

Error Summary:

Error Message Occurrences
ReadNanoTDF error: getNanoRewrapKey: rewrapError: internal: internal error
rpc error: code = Internal desc = could not perform access
5000 occurrences

Standard Benchmark Metrics Skipped or Failed

@pflynn-virtru pflynn-virtru marked this pull request as ready for review May 12, 2025 21:20
@pflynn-virtru pflynn-virtru requested review from a team as code owners May 12, 2025 21:20
Copy link
Contributor

Benchmark results, click to expand

Benchmark Results:

Metric Value
Error rpc error: code = Internal desc = resource retrieval failed
Total Time 67.627679ms

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 0
Failed Decrypts 100
Total Time 164.676037ms
Throughput 0.00 requests/second

Error Summary

Error Message Occurrences
splitKey.unable to reconstruct split key: map[{http://localhost:8080 }:kao unwrap failed for split {http://localhost:8080 }: internal: internal error
rpc error: code = Internal desc = could not perform access]
kao unwrap failed for split {http://localhost:8080 }: internal: internal error
rpc error: code = Internal desc = could not perform access 100 occurrences

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 0
Failed Requests 5000
Concurrent Requests 50
Total Time 35.194080888s
Throughput 0.00 requests/second

Error Summary:

Error Message Occurrences
read error: reader.WriteTo failed: splitKey.unable to reconstruct split key: map[{http://localhost:8080 }:kao unwrap failed for split {http://localhost:8080 }: internal: internal error
rpc error: code = Internal desc = could not perform access]
kao unwrap failed for split {http://localhost:8080 }: internal: internal error
rpc error: code = Internal desc = could not perform access
5000 occurrences

NANOTDF Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 0
Failed Requests 5000
Concurrent Requests 50
Total Time 26.498638549s
Throughput 0.00 requests/second

Error Summary:

Error Message Occurrences
ReadNanoTDF error: getNanoRewrapKey: rewrapError: internal: internal error
rpc error: code = Internal desc = could not perform access
5000 occurrences

Standard Benchmark Metrics Skipped or Failed

@pflynn-virtru pflynn-virtru changed the title feat: cmd for bulk keycloak provisioning feat: bulk keycloak provisioning May 12, 2025
Copy link
Contributor

Benchmark results, click to expand

Benchmark Results:

Metric Value
Approved Decision Requests 5000
Denied Decision Requests 0
Total Time 468.994904ms

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 353.420283ms
Throughput 282.95 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 1m15.348762392s
Average Latency 749.742508ms
Throughput 66.36 requests/second

NANOTDF Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 4948
Failed Requests 52
Concurrent Requests 50
Total Time 1m7.712678499s
Average Latency 671.024739ms
Throughput 73.07 requests/second

Error Summary:

Error Message Occurrences
ReadNanoTDF error: getNanoRewrapKey: rewrapError: internal: internal error
rpc error: code = Internal desc = could not perform access
52 occurrences

Standard Benchmark Metrics Skipped or Failed

@pflynn-virtru pflynn-virtru enabled auto-merge May 13, 2025 16:13
@pflynn-virtru pflynn-virtru force-pushed the feature/keycloak-duplicates branch from 353be13 to b04df10 Compare May 13, 2025 16:24
Copy link
Contributor

Benchmark results, click to expand

Benchmark Results:

Metric Value
Approved Decision Requests 5000
Denied Decision Requests 0
Total Time 464.27105ms

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 373.622957ms
Throughput 267.65 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 1m21.85720147s
Average Latency 816.165999ms
Throughput 61.08 requests/second

NANOTDF Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 4947
Failed Requests 53
Concurrent Requests 50
Total Time 1m12.200883653s
Average Latency 717.589888ms
Throughput 68.52 requests/second

Error Summary:

Error Message Occurrences
ReadNanoTDF error: getNanoRewrapKey: rewrapError: internal: internal error
rpc error: code = Internal desc = could not perform access
53 occurrences

Standard Benchmark Metrics Skipped or Failed

Copy link
Member

@jrschumacher jrschumacher left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Backwards compatibility change with custom keycloak yaml files

Copy link
Contributor

Benchmark results, click to expand

Benchmark Results:

Metric Value
Approved Decision Requests 5000
Denied Decision Requests 0
Total Time 449.52195ms

Bulk Benchmark Results

Metric Value
Total Decrypts 100
Successful Decrypts 100
Failed Decrypts 0
Total Time 363.529656ms
Throughput 275.08 requests/second

TDF3 Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 5000
Failed Requests 0
Concurrent Requests 50
Total Time 1m17.385616493s
Average Latency 771.108156ms
Throughput 64.61 requests/second

NANOTDF Benchmark Results:

Metric Value
Total Requests 5000
Successful Requests 4975
Failed Requests 25
Concurrent Requests 50
Total Time 1m8.886585135s
Average Latency 687.074798ms
Throughput 72.22 requests/second

Error Summary:

Error Message Occurrences
ReadNanoTDF error: getNanoRewrapKey: rewrapError: internal: internal error
rpc error: code = Internal desc = could not perform access
25 occurrences

Standard Benchmark Metrics Skipped or Failed

@pflynn-virtru pflynn-virtru added this pull request to the merge queue May 15, 2025
Merged via the queue into main with commit 59e4485 May 15, 2025
29 checks passed
@pflynn-virtru pflynn-virtru deleted the feature/keycloak-duplicates branch May 15, 2025 17:14
alkalescent pushed a commit that referenced this pull request May 19, 2025
Introduce Support for Multiple Users and Clients in Keycloak Setup

#### Overview
This PR enhances the Keycloak setup functionality by introducing support
for creating multiple users and clients dynamically. It modifies the
`SetupCustomKeycloak` function and updates the Keycloak data structure
to allow specifying a count for users and clients, ensuring scalability
and flexibility in managing Keycloak resources.

```yaml
- user
  copies: 100
- client
  copies: 10
```

results in an additional 100 users with -000 -- -999 prepended 

#### Key Changes
1. **Updated `RealmToCreate` Structure**:
   - Replaced `Users` field type from `[]gocloak.User` to `[]User`.
- Introduced a new `User` struct that extends `gocloak.User` with an
additional `Count` field.
- Added a `Count` field to the `Client` struct to allow creating
multiple clients.

2. **Dynamic User and Client Creation**:
   - Modified the `SetupCustomKeycloak` function:
- Added logic to iterate and create multiple users and clients based on
the provided `Count` value.
- Implemented padding for generated user IDs and usernames, as well as
client IDs, to ensure proper formatting.

3. **Mathematical Calculations**:
- Introduced `math` package for calculating the number of digits
required for padding user and client identifiers.

4. **Configuration Changes**:
- Updated `keycloak_data.yaml` to include the new `count` field for
clients and users under `realms`.

5. **Code Annotations**:
- Added a `//nolint:nestif` comment to suppress linting errors for
nested if-statements in the client creation logic.

#### Code Example
- **Dynamic Client Creation**:
  ```go
  numDigits := int(math.Log10(float64(customClient.Count-1))) + 1
  padFormat := fmt.Sprintf("%%s-%%%dd", numDigits)
  for i := 0; i < customClient.Count; i++ {
customClient.Client.ClientID = gocloak.StringP(fmt.Sprintf(padFormat,
customClient.Client.ClientID, i))
_, err = createClient(ctx, client, token, &kcConnectParams,
customClient.Client, realmRoles, clientRoleMap)
      if err != nil {
          return err
      }
  }
  ```

- **Dynamic User Creation**:
  ```go
  numDigits := int(math.Log10(float64(customUser.Count-1))) + 1
  padFormat := fmt.Sprintf("%%s-%%%dd", numDigits)
  for i := 0; i < customUser.Count; i++ {
customUser.User.ID = gocloak.StringP(fmt.Sprintf(padFormat,
customUser.User.ID, i))
customUser.User.Username = gocloak.StringP(fmt.Sprintf(padFormat,
customUser.User.Username, i))
_, err = createUser(ctx, client, token, &kcConnectParams,
customUser.User)
      if err != nil {
          return err
      }
  }
  ```

#### Benefits
- **Scalability**: Enables creating multiple users and clients
programmatically, reducing manual configuration overhead.
- **Flexibility**: Simplifies Keycloak setup for scenarios requiring a
large number of users or clients.
- **Improved Configuration**: Introduces a more streamlined and dynamic
approach to managing Keycloak data through YAML configuration.

#### Files Modified
1. `lib/fixtures/keycloak.go`:
   - Updated structures and logic for user and client creation.
2. `service/cmd/keycloak_data.yaml`:
   - Added `count` fields to sample configuration.

#### Testing
- Verified the creation of multiple users and clients with correct
padding using updated YAML configuration.
- Confirmed backward compatibility for existing setups without the
`count` field.

#### Notes
- The `count` field defaults to `1` if not provided.
- Linting exceptions were added where necessary to maintain readability
and functionality.

### Checklist

- [ ] I have added or updated unit tests
- [ ] I have added or updated integration tests (if appropriate)
- [ ] I have added or updated documentation

### Testing Instructions
github-merge-queue bot pushed a commit that referenced this pull request May 20, 2025
🤖 I have created a release *beep* *boop*
---


##
[0.3.0](lib/fixtures/v0.2.10...lib/fixtures/v0.3.0)
(2025-05-20)


### ⚠ BREAKING CHANGES

* **core:** Require go 1.23+
([#1979](#1979))

### Features

* bulk keycloak provisioning
([#2205](#2205))
([59e4485](59e4485))
* **core:** Require go 1.23+
([#1979](#1979))
([164c922](164c922))


### Bug Fixes

* **deps:** bump toolchain in /lib/fixtures and /examples to resolve CVE
GO-2025-3563 ([#2061](#2061))
([9c16843](9c16843))
* perfsprint lint issues
([#2209](#2209))
([7cf8b53](7cf8b53))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: opentdf-automation[bot] <149537512+opentdf-automation[bot]@users.noreply.github.com>
github-merge-queue bot pushed a commit that referenced this pull request May 22, 2025
🤖 I have created a release *beep* *boop*
---


##
[0.5.3](service/v0.5.2...service/v0.5.3)
(2025-05-22)


### Features

* **authz:** authz v2 versioning implementation
([#2173](#2173))
([557fc21](557fc21))
* **authz:** authz v2, ers v2 protos and gencode for ABAC with actions &
registered resource
([#2124](#2124))
([ea7992a](ea7992a))
* **authz:** export entity id prefix constant from entity instead of
authorization service v1
([#2261](#2261))
([94079a9](94079a9))
* **authz:** subject mapping plugin support for ABAC with actions
([#2223](#2223))
([d08b939](d08b939))
* bulk keycloak provisioning
([#2205](#2205))
([59e4485](59e4485))
* **core:** add otel to opentdf services
([#1858](#1858))
([53a7aa0](53a7aa0))
* **core:** Adds EC withSalt options
([#2126](#2126))
([67b6fb8](67b6fb8))
* **core:** enhance db configuration options
([#2285](#2285))
([ed9ff59](ed9ff59))
* **core:** New Key Index and Manager Plugin SPI
([#2095](#2095))
([eb446fc](eb446fc))
* **core:** support onConfigUpdate hook when registering services
([#1992](#1992))
([366d4dc](366d4dc))
* **core:** v2 ERS with proto updates
([#2210](#2210))
([a161ef8](a161ef8))
* **policy:** actions crud service endpoints and proto validation
([#2037](#2037))
([e933fa9](e933fa9))
* **policy:** actions service RPCs should actually hit storage layer
CRUD ([#2063](#2063))
([da4faf5](da4faf5))
* **policy:** add enhanced standard/custom actions protos
([#2020](#2020))
([bbac53f](bbac53f))
* **policy:** Add platform key indexer.
([#2189](#2189))
([861ef8d](861ef8d))
* **policy:** consume lib/identifier parse function
([#2181](#2181))
([1cef22b](1cef22b))
* **policy:** DSPX-1018 NDR retrieval by FQN support
([#2131](#2131))
([0001041](0001041))
* **policy:** DSPX-1057 registered resource action attribute values (DB
+ Service implementation)
([#2191](#2191))
([6bf1b2e](6bf1b2e))
* **policy:** DSPX-1057 registered resource action attribute values
(protos only) ([#2217](#2217))
([6375596](6375596))
* **policy:** DSPX-893 NDR define crud protos
([#2056](#2056))
([55a5c27](55a5c27))
* **policy:** DSPX-898 NDR database schema
([#2055](#2055))
([2a10a6a](2a10a6a))
* **policy:** DSPX-901 NDR database crud
([#2071](#2071))
([20e0a5f](20e0a5f))
* **policy:** DSPX-902 NDR service crud implementation (2/2)
([#2066](#2066))
([030ad33](030ad33))
* **policy:** DSPX-902 NDR service crud protos only (1/2)
([#2092](#2092))
([24b6cb5](24b6cb5))
* **policy:** Finish resource mapping groups
([#2224](#2224))
([5ff754e](5ff754e))
* **policy:** GetMatchedSubjectMappings should provide value FQN
([#2151](#2151))
([ad80044](ad80044))
* **policy:** key management crud
([#2110](#2110))
([4c3d53d](4c3d53d))
* **policy:** Key management proto
([#2115](#2115))
([561f853](561f853))
* **policy:** Modify get request to search for keys by kasid with keyid.
([#2147](#2147))
([780d2e4](780d2e4))
* **policy:** Restrict KAS deletion when tied to Key
([#2144](#2144))
([4c4ab13](4c4ab13))
* **policy:** Return KAS Key structure
([#2172](#2172))
([7f97b99](7f97b99))
* **policy:** rotate keys rpc
([#2180](#2180))
([0d00743](0d00743))
* **policy:** stored enhanced actions database migration, CRUD queries,
SM updates ([#2040](#2040))
([e6b7c79](e6b7c79))
* **sdk:** Add a KAS allowlist
([#2085](#2085))
([d7cfdf3](d7cfdf3))
* **sdk:** add nanotdf plaintext policy
([#2182](#2182))
([e5c56db](e5c56db))
* **sdk:** Use ConnectRPC in the go client
([#2200](#2200))
([fc34ee6](fc34ee6))


### Bug Fixes

* **core:** access pdp cleanup before actions in ABAC decisioning
([#2123](#2123))
([9b38a3c](9b38a3c))
* **core:** Autobump service
([#2080](#2080))
([006c724](006c724))
* **core:** Autobump service
([#2104](#2104))
([1f72cc7](1f72cc7))
* **core:** Autobump service
([#2108](#2108))
([be5b7d7](be5b7d7))
* **core:** bump to go 1.24 and bump service proto module dependencies
([#2064](#2064))
([94891a0](94891a0))
* **core:** Fix DPoP with grpc-gateway
([#2044](#2044))
([4483ef2](4483ef2))
* **core:** fix service go.mod
([#2141](#2141))
([3b98f6d](3b98f6d))
* **core:** Improves errors when under heavy load
([#2132](#2132))
([4490a14](4490a14))
* **core:** Let legacy KAOs use new trust plugins
([#2218](#2218))
([5aa6916](5aa6916))
* **core:** migrate from mitchellh/mapstructure to go-viper/mapstructure
([#2087](#2087))
([0a3a82e](0a3a82e))
* **core:** update viper to 1.20.1
([#2088](#2088))
([09099e9](09099e9))
* **core:** Updates vulnerable dep go/x/net
([#2072](#2072))
([11c02cd](11c02cd))
* **deps:** bump github.com/creasty/defaults from 1.7.0 to 1.8.0 in
/service ([#2242](#2242))
([86a9b46](86a9b46))
* **deps:** bump github.com/jackc/pgx/v5 from 5.5.5 to 5.7.5 in /service
([#2249](#2249))
([d8f3b67](d8f3b67))
* **deps:** bump the internal group across 1 directory with 2 updates
([#2296](#2296))
([7f92c70](7f92c70))
* **deps:** bump toolchain in /lib/fixtures and /examples to resolve CVE
GO-2025-3563 ([#2061](#2061))
([9c16843](9c16843))
* handle empty private and public key ctx structs
([#2272](#2272))
([f3fc647](f3fc647))
* **policy:** remove predefined rules in actions protos
([#2069](#2069))
([060f059](060f059))
* **policy:** return kas uri on keys for definition, namespace and
values ([#2186](#2186))
([6c55fb8](6c55fb8))
* update key_mode to provide more context
([#2226](#2226))
([44d0805](44d0805))

---
This PR was generated with [Release
Please](https://github.com/googleapis/release-please). See
[documentation](https://github.com/googleapis/release-please#release-please).

Co-authored-by: opentdf-automation[bot] <149537512+opentdf-automation[bot]@users.noreply.github.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

3 participants