Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
4321630
WIP : directory sync | keycloak integration
manab-pr May 15, 2026
f137325
Merge branch 'main' of https://github.com/manab-pr/core into feature/…
manab-pr May 19, 2026
1b18454
Merge branch 'main' of https://github.com/manab-pr/core into feature/…
manab-pr May 23, 2026
2a5ca09
feat : added keycloak integration files
manab-pr May 27, 2026
1768d78
Merge branch 'main' of https://github.com/manab-pr/core into feature/…
manab-pr May 27, 2026
87ab01d
post merge changes
manab-pr May 27, 2026
199835d
feat : containerized
manab-pr May 28, 2026
11f1c6c
Merge branch 'main' into feature/keycloak
manab-pr May 28, 2026
ac35234
Merge branch 'main' into feature/keycloak
manab-pr May 28, 2026
86d8e31
updated
manab-pr Jun 4, 2026
694d4df
Merge branch 'main' of https://github.com/manab-pr/core into feature/…
manab-pr Jun 4, 2026
9a61690
Merge branch 'feature/keycloak' of https://github.com/manab-pr/core i…
manab-pr Jun 4, 2026
1e96a13
fix : requested changes
manab-pr Jun 4, 2026
37ed112
Merge branch 'main' of https://github.com/manab-pr/core into feature/…
manab-pr Jun 13, 2026
0d448ae
fix : test file
manab-pr Jun 14, 2026
24d789a
Merge branch 'main' of https://github.com/manab-pr/core into feature/…
manab-pr Jun 17, 2026
5d6e878
Update internal/integrations/definitions/keycloak/builder.go
manab-pr Jun 17, 2026
8ed4736
Update internal/integrations/definitions/keycloak/doc.go
manab-pr Jun 17, 2026
0ec1e1e
Apply suggestion from @golanglemonade
manab-pr Jun 17, 2026
1964b30
Merge branch 'feature/keycloak' of https://github.com/manab-pr/core i…
manab-pr Jun 17, 2026
815d7da
added permissions, last login time
manab-pr Jun 17, 2026
eb2b76a
Merge branch 'main' of https://github.com/manab-pr/core into feature/…
manab-pr Jun 17, 2026
f909b69
fix : conflicts
manab-pr Jun 20, 2026
11bc578
Update docker/docker-compose-keycloak.yml
manab-pr Jun 23, 2026
62e206d
Merge branch 'main' of https://github.com/manab-pr/core into feature/…
manab-pr Jun 23, 2026
dc9746d
Merge branch 'main' into feature/keycloak
manab-pr Jun 24, 2026
4bba065
fix : ssl required NONE
manab-pr Jul 2, 2026
2910547
Merge branch 'main' of https://github.com/manab-pr/core into feature/…
manab-pr Jul 2, 2026
46539ef
Merge branch 'feature/keycloak' of https://github.com/manab-pr/core i…
manab-pr Jul 2, 2026
1edda28
fix : keycloak compose file
manab-pr Jul 2, 2026
b4fde76
Merge branch 'theopenlane:main' into feature/keycloak
manab-pr Jul 7, 2026
146667b
Merge branch 'main' of https://github.com/manab-pr/core into feature/…
manab-pr Jul 7, 2026
2adf037
Merge branch 'feature/keycloak' of https://github.com/manab-pr/core i…
manab-pr Jul 7, 2026
c9d7239
Update internal/integrations/definitions/keycloak/builder.go
manab-pr Jul 7, 2026
1cb3224
Update internal/integrations/definitions/keycloak/types.go
manab-pr Jul 7, 2026
95e7756
fix : added missing logs, and fixed the type
manab-pr Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions docker/Taskfile.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -210,3 +210,29 @@ tasks:
cmds:
- task: authentik
- task: authentik:open

keycloak:
dir: ..
desc: brings up the compose environment for keycloak
cmds:
- "docker compose -f ./docker/docker-compose-pg.yml -f ./docker/docker-compose-keycloak.yml up -d"

keycloak:down:
dir: ..
desc: brings the keycloak compose environment down
cmds:
- "docker compose -f ./docker/docker-compose-pg.yml -f ./docker/docker-compose-keycloak.yml down"

keycloak:open:
dir: ..
desc: opens the keycloak admin console in a browser
cmds:
- 'open "http://localhost:8180"'

keycloak:up:
dir: ..
desc: brings the keycloak compose environment up and opens the admin console
aliases: [keycloakup]
cmds:
- task: keycloak
- task: keycloak:open
4 changes: 4 additions & 0 deletions docker/configs/keycloak/.env-example
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
KC_BOOTSTRAP_ADMIN_USERNAME=admin
KC_BOOTSTRAP_ADMIN_PASSWORD=change-me
KC_DB_PASSWORD=password
KC_HOSTNAME=localhost
3 changes: 3 additions & 0 deletions docker/configs/keycloak/init.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
CREATE DATABASE keycloak;

GRANT ALL PRIVILEGES ON DATABASE keycloak TO postgres;
44 changes: 44 additions & 0 deletions docker/docker-compose-keycloak.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
services:
postgres:
volumes:
- ./configs/keycloak/init.sql:/docker-entrypoint-initdb.d/init_keycloak.sql

keycloak:
image: quay.io/keycloak/keycloak:latest
command: start-dev
env_file:
- ${PWD}/docker/configs/keycloak/.env-example
environment:
KC_DB: postgres
KC_DB_URL: jdbc:postgresql://postgres:5432/keycloak
KC_DB_USERNAME: postgres
KC_HOSTNAME: ${KC_HOSTNAME:-localhost}
KC_HTTP_PORT: 8180
ports:
- 8180:8180
depends_on:
postgres:
condition: service_healthy
healthcheck:
test: ["CMD-SHELL", "exec 3<>/dev/tcp/localhost/8180"]
interval: 10s
timeout: 5s
retries: 12
start_period: 30s
networks:
- default

keycloak-init:
image: quay.io/keycloak/keycloak:latest
depends_on:
keycloak:
condition: service_healthy
env_file:
- ${PWD}/docker/configs/keycloak/.env-example
entrypoint:
- sh
- -c
- "/opt/keycloak/bin/kcadm.sh config credentials --server http://keycloak:8180 --realm master --user $$KC_BOOTSTRAP_ADMIN_USERNAME --password $$KC_BOOTSTRAP_ADMIN_PASSWORD --client admin-cli && /opt/keycloak/bin/kcadm.sh update realms/master --set sslRequired=NONE"
networks:
- default
restart: "no"
Empty file.
6 changes: 5 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,8 @@ require (
github.com/AfterShip/email-verifier v1.4.1
github.com/Azure/azure-sdk-for-go/sdk/azcore v1.22.0
github.com/Azure/azure-sdk-for-go/sdk/azidentity v1.13.1
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity v0.15.0
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity v0.14.0
github.com/Nerzal/gocloak/v13 v13.9.0
github.com/PuerkitoBio/goquery v1.12.0
github.com/alicebob/miniredis/v2 v2.38.0
github.com/alitto/pond/v2 v2.7.1
Expand Down Expand Up @@ -136,6 +137,7 @@ require (
github.com/Masterminds/sprig/v3 v3.3.0 // indirect
github.com/andybalholm/cascadia v1.3.4 // indirect
github.com/coder/websocket v1.8.14 // indirect
github.com/go-resty/resty/v2 v2.7.0 // indirect
github.com/google/go-github/v87 v87.0.0 // indirect
github.com/hhrutter/lzw v1.0.0 // indirect
github.com/hhrutter/pkcs7 v0.2.2 // indirect
Expand All @@ -151,11 +153,13 @@ require (
github.com/microsoftgraph/msgraph-sdk-go-core v1.4.1 // indirect
github.com/moby/moby/api v1.54.2 // indirect
github.com/moby/moby/client v0.4.1 // indirect
github.com/opentracing/opentracing-go v1.2.0 // indirect
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 // indirect
github.com/pb33f/ordered-map/v2 v2.3.1 // indirect
github.com/philhofer/fwd v1.2.0 // indirect
github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c // indirect
github.com/santhosh-tekuri/jsonschema/v6 v6.0.2 // indirect
github.com/segmentio/ksuid v1.0.4 // indirect
github.com/sendgrid/rest v2.6.9+incompatible // indirect
github.com/sendgrid/sendgrid-go v3.16.1+incompatible // indirect
github.com/shopspring/decimal v1.4.0 // indirect
Expand Down
18 changes: 16 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,8 @@ github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2 h1:yz1bePFlP5Vws5+
github.com/Azure/azure-sdk-for-go/sdk/azidentity/cache v0.3.2/go.mod h1:Pa9ZNPuoNu/GztvBSKk9J1cDJW6vk/n0zLtV4mgd8N8=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0 h1:fhqpLE3UEXi9lPaBRpQ6XuRW0nU7hgg4zlmZZa+a9q4=
github.com/Azure/azure-sdk-for-go/sdk/internal v1.12.0/go.mod h1:7dCRMLwisfRH3dBupKeNCioWYUZ4SS09Z14H+7i8ZoY=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity v0.15.0 h1:5tRv65ZzstkUCQYFAcw2XtXkqxJdJ0ut1T0gyU1JB34=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity v0.15.0/go.mod h1:+ysYq16aHhv16EXlLgzTgbXvIQTCPdEblyyFad42JEo=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity v0.14.0 h1:JfjIyBJvEvQNP/9MEUo1/6eoiPkiag2OZImw32xakcc=
github.com/Azure/azure-sdk-for-go/sdk/resourcemanager/security/armsecurity v0.14.0/go.mod h1:HakuHOrWlp2G1WlFvkL7JApTZAbxRJnRiz+w4SYak5s=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c h1:udKWzYgxTojEKWjV8V+WSxDXJ4NFATAsZjh8iIbsQIg=
github.com/Azure/go-ansiterm v0.0.0-20250102033503-faa5f7b0171c/go.mod h1:xomTg63KZ2rFqZQzSB4Vz2SUXa1BpHTVz9L5PTmPC4E=
github.com/AzureAD/microsoft-authentication-extensions-for-go/cache v0.1.1 h1:WJTmL004Abzc5wDB5VtZG2PJk5ndYDgVacGqfirKxjM=
Expand All @@ -58,6 +58,8 @@ github.com/Masterminds/sprig/v3 v3.3.0 h1:mQh0Yrg1XPo6vjYXgtf5OtijNAKJRNcTdOOGZe
github.com/Masterminds/sprig/v3 v3.3.0/go.mod h1:Zy1iXRYNqNLUolqCpL4uhk6SHUMAOSCzdgBfDb35Lz0=
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/Nerzal/gocloak/v13 v13.9.0 h1:YWsJsdM5b0yhM2Ba3MLydiOlujkBry4TtdzfIzSVZhw=
github.com/Nerzal/gocloak/v13 v13.9.0/go.mod h1:YYuDcXZ7K2zKECyVP7pPqjKxx2AzYSpKDj8d6GuyM10=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5 h1:TngWCqHvy9oXAN6lEVMRuU21PR1EtLVZJmdB18Gu3Rw=
github.com/Nvveen/Gotty v0.0.0-20120604004816-cd527374f1e5/go.mod h1:lmUJ/7eu/Q8D7ML55dXQrVaamCz2vxCfdQBasLZfHKk=
github.com/PuerkitoBio/goquery v1.12.0 h1:pAcL4g3WRXekcB9AU/y1mbKez2dbY2AajVhtkO8RIBo=
Expand Down Expand Up @@ -290,6 +292,8 @@ github.com/go-redis/redis/v8 v8.11.5 h1:AcZZR7igkdvfVmQTPnu9WE37LRrO/YrBH5zWyjDC
github.com/go-redis/redis/v8 v8.11.5/go.mod h1:gREzHqY1hg6oD9ngVRbLStwAWKhA0FEgq8Jd4h5lpwo=
github.com/go-redis/redismock/v8 v8.0.6 h1:rtuijPgGynsRB2Y7KDACm09WvjHWS4RaG44Nm7rcj4Y=
github.com/go-redis/redismock/v8 v8.0.6/go.mod h1:sDIF73OVsmaKzYe/1FJXGiCQ4+oHYbzjpaL9Vor0sS4=
github.com/go-resty/resty/v2 v2.7.0 h1:me+K9p3uhSmXtrBZ4k9jcEAfJmuC8IivWHwaLZwPrFY=
github.com/go-resty/resty/v2 v2.7.0/go.mod h1:9PWDzw47qPphMRFfhsyk0NnSgvluHcljSMVIq3w7q0I=
github.com/go-test/deep v1.1.1 h1:0r/53hagsehfO4bzD2Pgr/+RgHqhmf+k1Bpse2cTu1U=
github.com/go-test/deep v1.1.1/go.mod h1:5C2ZWiW0ErCdrYzpqxLbTX7MG14M9iiw8DgHncVwcsE=
github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
Expand Down Expand Up @@ -574,6 +578,8 @@ github.com/openfga/language/pkg/go v0.3.0 h1:Ln0Cd3H/ych9YLErp5YlYNwf3PiQDfu0u+G
github.com/openfga/language/pkg/go v0.3.0/go.mod h1:7ctT4Q7erZ9RBzSsICUV94EUMdDEynomgG0Y9etTicI=
github.com/openfga/openfga v1.18.0 h1:aNQHPNZGDYyf4DqxdTzzTT/5nQM4yAtKw3S3ZFhSPn0=
github.com/openfga/openfga v1.18.0/go.mod h1:se83OqKfGcb9Ddw/Bk85d2WE7PisFmDv/CeZQh3ZyIM=
github.com/opentracing/opentracing-go v1.2.0 h1:uEJPy/1a5RIPAJ0Ov+OIO8OxWu77jEv+1B0VhjKrZUs=
github.com/opentracing/opentracing-go v1.2.0/go.mod h1:GxEUsuufX4nBwe+T+Wl9TAgYrxe9dPLANfrWvHYVTgc=
github.com/ory/dockertest v3.3.5+incompatible h1:iLLK6SQwIhcbrG783Dghaaa3WPzGc+4Emza6EbVUUGA=
github.com/ory/dockertest v3.3.5+incompatible/go.mod h1:1vX4m9wsvi00u5bseYwXaSnhNrne+V0E6LAcBILJdPs=
github.com/patrickmn/go-cache v0.0.0-20180815053127-5633e0862627 h1:pSCLCl6joCFRnjpeojzOpEYs4q7Vditq8fySFG5ap3Y=
Expand Down Expand Up @@ -655,6 +661,8 @@ github.com/scim2/filter-parser/v2 v2.2.0 h1:QGadEcsmypxg8gYChRSM2j1edLyE/2j72j+h
github.com/scim2/filter-parser/v2 v2.2.0/go.mod h1:jWnkDToqX/Y0ugz0P5VvpVEUKcWcyHHj+X+je9ce5JA=
github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0=
github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
github.com/segmentio/ksuid v1.0.4 h1:sBo2BdShXjmcugAMwjugoGUdUV0pcxY5mW4xKRn3v4c=
github.com/segmentio/ksuid v1.0.4/go.mod h1:/XUiZBD3kVx5SmUOl55voK5yeAbBNNIed+2O73XgrPE=
github.com/sendgrid/rest v2.6.9+incompatible h1:1EyIcsNdn9KIisLW50MKwmSRSK+ekueiEMJ7NEoxJo0=
github.com/sendgrid/rest v2.6.9+incompatible/go.mod h1:kXX7q3jZtJXK5c5qK83bSGMdV6tsOE70KbHoqJls4lE=
github.com/sendgrid/sendgrid-go v3.16.1+incompatible h1:zWhTmB0Y8XCDzeWIm2/BIt1GjJohAA0p6hVEaDtHWWs=
Expand Down Expand Up @@ -865,6 +873,7 @@ golang.org/x/image v0.41.0/go.mod h1:uIc348UZMSvS5Z65CVZ7iDPaNobNFEPeJ4kbqTOszmA
golang.org/x/mod v0.37.0 h1:vF1DjpVEshcIqoEaauuHebaLk1O1forxjxBaVn884JQ=
golang.org/x/mod v0.37.0/go.mod h1:m8S8VeM9r4dzDwjrKO0a1sZP3YjeMamRRlD+fmR2Q/0=
golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
golang.org/x/net v0.0.0-20211029224645-99673261e6eb/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.56.0 h1:Rw8j/hFzGvJUZwNBXnAtf5sVDVt+65SK2C7IxCxZt5o=
golang.org/x/net v0.56.0/go.mod h1:D3Ku6r+V6JROoZK144D2XfMHFcMq/0zSfLelVTCFKec=
golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
Expand All @@ -875,20 +884,25 @@ golang.org/x/sync v0.21.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20181122145206-62eef0e2fa9b/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190916202348-b4ddaad3f8a3/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201204225414-ed752295db88/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210616094352-59db8d763f22/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220310020820-b874c991c1a5/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.1.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.46.0 h1:noSf2Fq6F8DBgS+LysIkx7rIExoNHJsxOAtPp4rthXw=
golang.org/x/sys v0.46.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.44.0 h1:0rLvDRCtNj0gZkyIXhCyOb2OAzEhLVqc4B+hrsBhrmc=
golang.org/x/term v0.44.0/go.mod h1:7ze4MdzUzLXpSAoFP1H0bOI9aXDqveSvatT5vKcFh2Y=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.38.0 h1:sXmwo9DwP3OK9EZ7PqAdaooSGozfl/3a6/xJcbzPRhE=
golang.org/x/text v0.38.0/go.mod h1:YXZt3QhHUKYT53r2lLKFIVi6Ao1jdzrTR/KQ09qyxF4=
golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.46.0 h1:7jTurBkPZu4moS/Uy4OQT1M+QBlsj3wejyZwsT8Z7rk=
golang.org/x/tools v0.46.0/go.mod h1:FrD85F8l+NWL+9XWBSyVSHO6Ne4jutsfIFba7AWQ5Ys=
golang.org/x/xerrors v0.0.0-20240903120638-7835f813f4da h1:noIWHXmPHxILtqtCOPIhSt0ABwskkZKjD3bXGnZGpNY=
Expand Down
2 changes: 2 additions & 0 deletions internal/integrations/definitions/catalog/catalog.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"github.com/theopenlane/core/internal/integrations/definitions/githubapp"
"github.com/theopenlane/core/internal/integrations/definitions/googledrive"
"github.com/theopenlane/core/internal/integrations/definitions/googleworkspace"
"github.com/theopenlane/core/internal/integrations/definitions/keycloak"
"github.com/theopenlane/core/internal/integrations/definitions/microsoftteams"
"github.com/theopenlane/core/internal/integrations/definitions/oidclocal"
"github.com/theopenlane/core/internal/integrations/definitions/okta"
Expand All @@ -36,6 +37,7 @@ func Builders(cfg Config, devMode bool) []registry.Builder {
githubapp.Builder(cfg.GitHubApp),
googledrive.Builder(cfg.GoogleDrive),
googleworkspace.Builder(cfg.GoogleWorkspace),
keycloak.Builder(),
microsoftteams.Builder(cfg.MicrosoftTeams),
onedrive.Builder(cfg.OneDrive),
oidclocal.Builder(cfg.OIDCLocal),
Expand Down
96 changes: 96 additions & 0 deletions internal/integrations/definitions/keycloak/builder.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
package keycloak

import (
"github.com/theopenlane/core/internal/ent/integrationgenerated"
"github.com/theopenlane/core/internal/integrations/registry"
"github.com/theopenlane/core/internal/integrations/types"
"github.com/theopenlane/core/pkg/jsonx"
)

// Builder returns the Keycloak definition builder
func Builder() registry.Builder {
return registry.Builder(func() (types.Definition, error) {
return types.Definition{
DefinitionSpec: types.DefinitionSpec{
ID: definitionID.ID(),
Family: "Keycloak",
DisplayName: "Keycloak",
Description: "Collect Keycloak realm users, groups, and memberships for identity posture and access governance.",
Category: "identity",
DocsURL: "https://docs.theopenlane.io/docs/platform/integrations/keycloak/overview",
Tags: []string{"directory"},
Active: false,
Visible: true,
},
UserInput: &types.UserInputRegistration{
Schema: jsonx.SchemaFrom[UserInput](),
},
CredentialRegistrations: []types.CredentialRegistration{
{
Ref: keycloakCredential.ID(),
Name: "Keycloak Credential",
Description: "Client credentials used to access Keycloak realm data.",
Schema: keycloakCredentialSchema,
},
},
Connections: []types.ConnectionRegistration{
{
CredentialRef: keycloakCredential.ID(),
Name: "Keycloak Client Credentials",
Description: "Configure Keycloak access using client credentials from your realm.",
CredentialRefs: []types.CredentialSlotID{keycloakCredential.ID()},
ClientRefs: []types.ClientID{keycloakClient.ID()},
ValidationOperation: healthCheckOperation.Name(),
Integration: integration.Registration(),
Disconnect: &types.DisconnectRegistration{
CredentialRef: keycloakCredential.ID(),
Description: "Removes the stored client credentials from Openlane. If the client is no longer needed, disable or delete it in your Keycloak admin console under Clients.",
},
},
},
Clients: []types.ClientRegistration{
{
Ref: keycloakClient.ID(),
CredentialRefs: []types.CredentialSlotID{keycloakCredential.ID()},
Description: "Keycloak API client",
Build: Client{}.Build,
},
},
Operations: []types.OperationRegistration{
{
Name: healthCheckOperation.Name(),
Description: "Call Keycloak realm API to verify client credentials and realm connectivity",
Topic: definitionID.OperationTopic(healthCheckOperation.Name()),
ClientRef: keycloakClient.ID(),
Policy: types.ExecutionPolicy{Inline: true},
ConfigSchema: healthCheckSchema,
Handle: HealthCheck{}.Handle(),
RequiredPermissions: []string{"view-realm"},
},
{
Name: directorySyncOperation.Name(),
Comment thread
manab-pr marked this conversation as resolved.
Description: "Collect Keycloak realm users, groups, and memberships as directory accounts",
Topic: definitionID.OperationTopic(directorySyncOperation.Name()),
ClientRef: keycloakClient.ID(),
ConfigSchema: directorySyncSchema,
Policy: types.ExecutionPolicy{Reconcile: true},
SkipDefaultLookback: true,
RequiredPermissions: []string{"view-realm", "view-users", "query-groups", "view-events"},
Ingest: []types.IngestContract{
{
Schema: integrationgenerated.IntegrationMappingSchemaDirectoryAccount,
},
{
Schema: integrationgenerated.IntegrationMappingSchemaDirectoryGroup,
},
{
Schema: integrationgenerated.IntegrationMappingSchemaDirectoryMembership,
},
},
IngestHandle: DirectorySync{}.IngestHandle(),
},
},
Mappings: keycloakMappings(),
}, nil
})
}
71 changes: 71 additions & 0 deletions internal/integrations/definitions/keycloak/client.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package keycloak

import (
"context"
"time"

gocloak "github.com/Nerzal/gocloak/v13"

"github.com/theopenlane/core/internal/integrations/types"
)

const (
// keycloakRequestTimeout is the per-request timeout for Keycloak API calls
keycloakRequestTimeout = 30 * time.Second
// keycloakDefaultPageSize is the number of records requested per Keycloak API page
keycloakDefaultPageSize = 100
// keycloakMaxLoginEvents is the maximum number of LOGIN events fetched when resolving last login times
keycloakMaxLoginEvents = 1000
)

// Client builds Keycloak API clients for one installation
type Client struct{}

// Build constructs the Keycloak API client for one installation
func (Client) Build(_ context.Context, req types.ClientBuildRequest) (any, error) {
cred, err := resolveCredential(req.Credentials)
if err != nil {
return nil, err
}

if cred.BaseURL == "" {
return nil, ErrBaseURLMissing
}

if cred.Realm == "" {
return nil, ErrRealmMissing
}

if cred.ClientID == "" {
return nil, ErrClientIDMissing
}

if cred.ClientSecret == "" {
return nil, ErrClientSecretMissing
}

gc := gocloak.NewClient(cred.BaseURL)
gc.RestyClient().SetTimeout(keycloakRequestTimeout)

return gc, nil
}

// resolveCredential extracts the CredentialSchema from the provided credential bindings
func resolveCredential(bindings types.CredentialBindings) (CredentialSchema, error) {
cred, ok, err := keycloakCredential.Resolve(bindings)
if err != nil {
return CredentialSchema{}, ErrCredentialDecode
}

if !ok {
return CredentialSchema{}, ErrCredentialDecode
}

return cred, nil
}

// enrichedUser wraps a Keycloak user with additional data not on the user object
type enrichedUser struct {
*gocloak.User
LastLogin *int64 `json:"lastLogin,omitempty"`
}
Loading