From 6ea7ca065b7e31e3f995de02ad9fdd459b900a5a Mon Sep 17 00:00:00 2001 From: Brian Ojeda <9335829+sgtoj@users.noreply.github.com> Date: Sun, 7 Dec 2025 19:52:31 -0500 Subject: [PATCH 1/2] feat: upgrade to okta sdk v6 --- fixtures/scenarios.json | 38 ++++++------- go.mod | 22 ++++--- go.sum | 59 +++++++++++++------ internal/okta/client.go | 123 ++++++++++++++++++++++++++++------------ internal/okta/groups.go | 57 ++++++++++++++----- 5 files changed, 206 insertions(+), 93 deletions(-) diff --git a/fixtures/scenarios.json b/fixtures/scenarios.json index 13c0887..0fce9b4 100644 --- a/fixtures/scenarios.json +++ b/fixtures/scenarios.json @@ -69,7 +69,7 @@ "method": "GET", "path": "/api/v1/groups", "status_code": 200, - "body": "[{\"id\":\"00g1234567890abcdef\",\"profile\":{\"name\":\"Engineering\",\"description\":\"Engineering team\"}}]", + "body": "[{\"id\": \"00g1234567890abcdef\", \"created\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"lastMembershipUpdated\": \"2020-01-01T00:00:00.000Z\", \"objectClass\": [\"okta:user_group\"], \"type\": \"OKTA_GROUP\", \"profile\": {\"name\": \"Engineering\", \"description\": \"Engineering team\"}}]", "description": "fetch all okta groups (returns engineering group)" }, { @@ -77,7 +77,7 @@ "method": "GET", "path": "/api/v1/groups/00g1234567890abcdef/users", "status_code": 200, - "body": "[{\"id\":\"00u1111111111111111\",\"status\":\"ACTIVE\",\"profile\":{\"email\":\"alice@example.com\",\"githubUsername\":\"alice-gh\"}}]", + "body": "[{\"id\": \"00u1111111111111111\", \"status\": \"ACTIVE\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"alice@example.com\", \"githubUsername\": \"alice-gh\"}}]", "description": "fetch users in engineering group (returns alice-gh)" }, { @@ -571,7 +571,7 @@ "method": "GET", "path": "/api/v1/groups", "status_code": 200, - "body": "[{\"id\":\"00g1234567890abcdef\",\"profile\":{\"name\":\"Engineering\",\"description\":\"Engineering team\"}}]", + "body": "[{\"id\": \"00g1234567890abcdef\", \"created\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"lastMembershipUpdated\": \"2020-01-01T00:00:00.000Z\", \"objectClass\": [\"okta:user_group\"], \"type\": \"OKTA_GROUP\", \"profile\": {\"name\": \"Engineering\", \"description\": \"Engineering team\"}}]", "description": "fetch all okta groups (returns engineering group)" }, { @@ -579,7 +579,7 @@ "method": "GET", "path": "/api/v1/groups/00g1234567890abcdef/users", "status_code": 200, - "body": "[{\"id\":\"00u1111111111111111\",\"status\":\"ACTIVE\",\"profile\":{\"email\":\"alice@example.com\",\"githubUsername\":\"alice-gh\"}},{\"id\":\"00u2222222222222222\",\"status\":\"SUSPENDED\",\"profile\":{\"email\":\"suspended@example.com\",\"githubUsername\":\"suspended-user\"}},{\"id\":\"00u3333333333333333\",\"status\":\"DEPROVISIONED\",\"profile\":{\"email\":\"inactive@example.com\",\"githubUsername\":\"inactive-user\"}}]", + "body": "[{\"id\": \"00u1111111111111111\", \"status\": \"ACTIVE\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"alice@example.com\", \"githubUsername\": \"alice-gh\"}}, {\"id\": \"00u2222222222222222\", \"status\": \"SUSPENDED\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"suspended@example.com\", \"githubUsername\": \"suspended-user\"}}, {\"id\": \"00u3333333333333333\", \"status\": \"DEPROVISIONED\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"inactive@example.com\", \"githubUsername\": \"inactive-user\"}}]", "description": "fetch users in engineering group (active, suspended, deprovisioned statuses)" }, { @@ -667,7 +667,7 @@ "method": "GET", "path": "/api/v1/groups", "status_code": 200, - "body": "[{\"id\":\"00g1234567890abcdef\",\"profile\":{\"name\":\"Engineering\",\"description\":\"Engineering team\"}}]", + "body": "[{\"id\": \"00g1234567890abcdef\", \"created\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"lastMembershipUpdated\": \"2020-01-01T00:00:00.000Z\", \"objectClass\": [\"okta:user_group\"], \"type\": \"OKTA_GROUP\", \"profile\": {\"name\": \"Engineering\", \"description\": \"Engineering team\"}}]", "description": "fetch all okta groups (returns engineering group)" }, { @@ -675,7 +675,7 @@ "method": "GET", "path": "/api/v1/groups/00g1234567890abcdef/users", "status_code": 200, - "body": "[{\"id\":\"00u1111111111111111\",\"status\":\"ACTIVE\",\"profile\":{\"email\":\"alice@example.com\",\"githubUsername\":\"alice-gh\"}}]", + "body": "[{\"id\": \"00u1111111111111111\", \"status\": \"ACTIVE\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"alice@example.com\", \"githubUsername\": \"alice-gh\"}}]", "description": "fetch users in engineering group (only alice-gh)" }, { @@ -750,7 +750,7 @@ "method": "GET", "path": "/api/v1/groups", "status_code": 200, - "body": "[{\"id\":\"00g1234567890abcdef\",\"profile\":{\"name\":\"Engineering\",\"description\":\"Engineering team\"}}]", + "body": "[{\"id\": \"00g1234567890abcdef\", \"created\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"lastMembershipUpdated\": \"2020-01-01T00:00:00.000Z\", \"objectClass\": [\"okta:user_group\"], \"type\": \"OKTA_GROUP\", \"profile\": {\"name\": \"Engineering\", \"description\": \"Engineering team\"}}]", "description": "fetch all okta groups (returns engineering group)" }, { @@ -808,7 +808,7 @@ "method": "GET", "path": "/api/v1/groups", "status_code": 200, - "body": "[{\"id\":\"00g1234567890abcdef\",\"profile\":{\"name\":\"Engineering\",\"description\":\"Engineering team\"}}]", + "body": "[{\"id\": \"00g1234567890abcdef\", \"created\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"lastMembershipUpdated\": \"2020-01-01T00:00:00.000Z\", \"objectClass\": [\"okta:user_group\"], \"type\": \"OKTA_GROUP\", \"profile\": {\"name\": \"Engineering\", \"description\": \"Engineering team\"}}]", "description": "fetch all okta groups (returns engineering group)" }, { @@ -883,7 +883,7 @@ "method": "GET", "path": "/api/v1/groups", "status_code": 200, - "body": "[{\"id\":\"00g1234567890abcdef\",\"profile\":{\"name\":\"Engineering\",\"description\":\"Engineering team\"}}]", + "body": "[{\"id\": \"00g1234567890abcdef\", \"created\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"lastMembershipUpdated\": \"2020-01-01T00:00:00.000Z\", \"objectClass\": [\"okta:user_group\"], \"type\": \"OKTA_GROUP\", \"profile\": {\"name\": \"Engineering\", \"description\": \"Engineering team\"}}]", "description": "fetch all okta groups (returns engineering group)" }, { @@ -891,7 +891,7 @@ "method": "GET", "path": "/api/v1/groups/00g1234567890abcdef/users", "status_code": 200, - "body": "[{\"id\":\"00u1111111111111111\",\"status\":\"ACTIVE\",\"profile\":{\"email\":\"alice@example.com\",\"githubUsername\":\"alice-gh\"}}]", + "body": "[{\"id\": \"00u1111111111111111\", \"status\": \"ACTIVE\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"alice@example.com\", \"githubUsername\": \"alice-gh\"}}]", "description": "fetch users in engineering group (returns alice-gh)" }, { @@ -998,7 +998,7 @@ "method": "GET", "path": "/api/v1/groups", "status_code": 200, - "body": "[{\"id\":\"00g1234567890abcdef\",\"profile\":{\"name\":\"Engineering\",\"description\":\"Engineering team\"}}]", + "body": "[{\"id\": \"00g1234567890abcdef\", \"created\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"lastMembershipUpdated\": \"2020-01-01T00:00:00.000Z\", \"objectClass\": [\"okta:user_group\"], \"type\": \"OKTA_GROUP\", \"profile\": {\"name\": \"Engineering\", \"description\": \"Engineering team\"}}]", "description": "fetch all okta groups (returns engineering group)" }, { @@ -1006,7 +1006,7 @@ "method": "GET", "path": "/api/v1/groups/00g1234567890abcdef/users", "status_code": 200, - "body": "[{\"id\":\"00u1111111111111111\",\"status\":\"ACTIVE\",\"profile\":{\"email\":\"alice@example.com\",\"githubUsername\":\"alice-gh\"}}]", + "body": "[{\"id\": \"00u1111111111111111\", \"status\": \"ACTIVE\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"alice@example.com\", \"githubUsername\": \"alice-gh\"}}]", "description": "fetch users in engineering group (returns only alice-gh)" }, { @@ -1105,7 +1105,7 @@ "method": "GET", "path": "/api/v1/groups", "status_code": 200, - "body": "[{\"id\":\"00g1234567890abcdef\",\"profile\":{\"name\":\"Engineering\",\"description\":\"Engineering team\"}}]", + "body": "[{\"id\": \"00g1234567890abcdef\", \"created\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"lastMembershipUpdated\": \"2020-01-01T00:00:00.000Z\", \"objectClass\": [\"okta:user_group\"], \"type\": \"OKTA_GROUP\", \"profile\": {\"name\": \"Engineering\", \"description\": \"Engineering team\"}}]", "description": "fetch all okta groups (returns engineering group)" }, { @@ -1113,7 +1113,7 @@ "method": "GET", "path": "/api/v1/groups/00g1234567890abcdef/users", "status_code": 200, - "body": "[{\"id\":\"00u1111111111111111\",\"status\":\"ACTIVE\",\"profile\":{\"email\":\"alice@example.com\",\"githubUsername\":\"alice-gh\"}},{\"id\":\"00u2222222222222222\",\"status\":\"ACTIVE\",\"profile\":{\"email\":\"bob@example.com\",\"githubUsername\":\"bob-gh\"}}]", + "body": "[{\"id\": \"00u1111111111111111\", \"status\": \"ACTIVE\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"alice@example.com\", \"githubUsername\": \"alice-gh\"}}, {\"id\": \"00u2222222222222222\", \"status\": \"ACTIVE\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"bob@example.com\", \"githubUsername\": \"bob-gh\"}}]", "description": "fetch users in engineering group (returns alice-gh and bob-gh)" }, { @@ -1191,7 +1191,7 @@ "method": "GET", "path": "/api/v1/groups", "status_code": 200, - "body": "[{\"id\":\"00g1234567890abcdef\",\"profile\":{\"name\":\"Engineering\",\"description\":\"Engineering team\"}}]", + "body": "[{\"id\": \"00g1234567890abcdef\", \"created\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"lastMembershipUpdated\": \"2020-01-01T00:00:00.000Z\", \"objectClass\": [\"okta:user_group\"], \"type\": \"OKTA_GROUP\", \"profile\": {\"name\": \"Engineering\", \"description\": \"Engineering team\"}}]", "description": "fetch all okta groups (returns engineering group)" }, { @@ -1199,7 +1199,7 @@ "method": "GET", "path": "/api/v1/groups/00g1234567890abcdef/users", "status_code": 200, - "body": "[{\"id\":\"00u1111111111111111\",\"status\":\"ACTIVE\",\"profile\":{\"email\":\"alice@example.com\",\"githubUsername\":\"alice-gh\"}}]", + "body": "[{\"id\": \"00u1111111111111111\", \"status\": \"ACTIVE\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"alice@example.com\", \"githubUsername\": \"alice-gh\"}}]", "description": "fetch users in engineering group (returns alice-gh)" }, { @@ -1287,7 +1287,7 @@ "method": "GET", "path": "/api/v1/groups", "status_code": 200, - "body": "[{\"id\":\"00g1234567890abcdef\",\"profile\":{\"name\":\"Engineering\",\"description\":\"Engineering team\"}}]", + "body": "[{\"id\": \"00g1234567890abcdef\", \"created\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"lastMembershipUpdated\": \"2020-01-01T00:00:00.000Z\", \"objectClass\": [\"okta:user_group\"], \"type\": \"OKTA_GROUP\", \"profile\": {\"name\": \"Engineering\", \"description\": \"Engineering team\"}}]", "description": "fetch all okta groups (returns engineering group)" }, { @@ -1295,7 +1295,7 @@ "method": "GET", "path": "/api/v1/groups/00g1234567890abcdef/users", "status_code": 200, - "body": "[{\"id\":\"00u1111111111111111\",\"status\":\"ACTIVE\",\"profile\":{\"email\":\"alice@example.com\",\"githubUsername\":\"alice-gh\"}},{\"id\":\"00u2222222222222222\",\"status\":\"ACTIVE\",\"profile\":{\"email\":\"bob@example.com\"}},{\"id\":\"00u3333333333333333\",\"status\":\"ACTIVE\",\"profile\":{\"email\":\"charlie@example.com\",\"githubUsername\":\"\"}}]", + "body": "[{\"id\": \"00u1111111111111111\", \"status\": \"ACTIVE\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"alice@example.com\", \"githubUsername\": \"alice-gh\"}}, {\"id\": \"00u2222222222222222\", \"status\": \"ACTIVE\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"bob@example.com\"}}, {\"id\": \"00u3333333333333333\", \"status\": \"ACTIVE\", \"created\": \"2020-01-01T00:00:00.000Z\", \"activated\": \"2020-01-01T00:00:00.000Z\", \"statusChanged\": \"2020-01-01T00:00:00.000Z\", \"lastLogin\": \"2020-01-01T00:00:00.000Z\", \"lastUpdated\": \"2020-01-01T00:00:00.000Z\", \"passwordChanged\": \"2020-01-01T00:00:00.000Z\", \"type\": {\"id\": \"oty1234567890\"}, \"profile\": {\"email\": \"charlie@example.com\", \"githubUsername\": \"\"}}]", "description": "fetch users in engineering group (alice has gh username, bob and charlie do not)" }, { @@ -1308,4 +1308,4 @@ } ] } -] +] \ No newline at end of file diff --git a/go.mod b/go.mod index b107641..b63beae 100644 --- a/go.mod +++ b/go.mod @@ -11,13 +11,12 @@ require ( github.com/golang-jwt/jwt/v5 v5.3.0 github.com/google/go-github/v79 v79.0.0 github.com/joho/godotenv v1.5.1 - github.com/okta/okta-sdk-golang/v2 v2.20.0 + github.com/okta/okta-sdk-golang/v6 v6.0.1 github.com/slack-go/slack v0.17.3 golang.org/x/oauth2 v0.33.0 ) require ( - github.com/BurntSushi/toml v1.2.1 // indirect github.com/aws/aws-sdk-go-v2/credentials v1.19.1 // indirect github.com/aws/aws-sdk-go-v2/feature/ec2/imds v1.18.14 // indirect github.com/aws/aws-sdk-go-v2/internal/configsources v1.4.14 // indirect @@ -30,22 +29,31 @@ require ( github.com/aws/aws-sdk-go-v2/service/ssooidc v1.35.9 // indirect github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 // indirect github.com/aws/smithy-go v1.23.2 // indirect - github.com/cenkalti/backoff/v4 v4.1.3 // indirect + github.com/cenkalti/backoff/v5 v5.0.3 // indirect github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b // indirect github.com/cockroachdb/redact v1.1.5 // indirect + github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 // indirect github.com/getsentry/sentry-go v0.27.0 // indirect - github.com/go-jose/go-jose/v3 v3.0.0 // indirect + github.com/go-jose/go-jose/v4 v4.1.3 // indirect + github.com/goccy/go-json v0.10.5 // indirect github.com/gogo/protobuf v1.3.2 // indirect github.com/google/go-querystring v1.1.0 // indirect + github.com/google/uuid v1.6.0 // indirect github.com/gorilla/websocket v1.5.3 // indirect github.com/kelseyhightower/envconfig v1.4.0 // indirect github.com/kr/pretty v0.3.1 // indirect github.com/kr/text v0.2.0 // indirect + github.com/lestrrat-go/blackmagic v1.0.4 // indirect + github.com/lestrrat-go/httpcc v1.0.1 // indirect + github.com/lestrrat-go/httprc/v3 v3.0.1 // indirect + github.com/lestrrat-go/jwx/v3 v3.0.12 // indirect + github.com/lestrrat-go/option v1.0.1 // indirect + github.com/lestrrat-go/option/v2 v2.0.0 // indirect github.com/patrickmn/go-cache v2.1.0+incompatible // indirect github.com/pkg/errors v0.9.1 // indirect github.com/rogpeppe/go-internal v1.9.0 // indirect - golang.org/x/crypto v0.7.0 // indirect - golang.org/x/sys v0.31.0 // indirect - golang.org/x/text v0.23.0 // indirect + github.com/segmentio/asm v1.2.1 // indirect + golang.org/x/sys v0.37.0 // indirect + golang.org/x/text v0.30.0 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect ) diff --git a/go.sum b/go.sum index 9f83337..a2ee53d 100644 --- a/go.sum +++ b/go.sum @@ -1,5 +1,3 @@ -github.com/BurntSushi/toml v1.2.1 h1:9F2/+DoOYIOksmaJFPw1tGFy1eDnIJXg+UHjuD8lTak= -github.com/BurntSushi/toml v1.2.1/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ= github.com/aws/aws-lambda-go v1.50.0 h1:0GzY18vT4EsCvIyk3kn3ZH5Jg30NRlgYaai1w0aGPMU= github.com/aws/aws-lambda-go v1.50.0/go.mod h1:dpMpZgvWx5vuQJfBt0zqBha60q7Dd7RfgJv23DymV8A= github.com/aws/aws-sdk-go-v2 v1.40.0 h1:/WMUA0kjhZExjOQN2z3oLALDREea1A7TobfuiBrKlwc= @@ -32,8 +30,8 @@ github.com/aws/aws-sdk-go-v2/service/sts v1.41.1 h1:GdGmKtG+/Krag7VfyOXV17xjTCz0 github.com/aws/aws-sdk-go-v2/service/sts v1.41.1/go.mod h1:6TxbXoDSgBQ225Qd8Q+MbxUxUh6TtNKwbRt/EPS9xso= github.com/aws/smithy-go v1.23.2 h1:Crv0eatJUQhaManss33hS5r40CG3ZFH+21XSkqMrIUM= github.com/aws/smithy-go v1.23.2/go.mod h1:LEj2LM3rBRQJxPZTB4KuzZkaZYnZPnvgIhb4pu07mx0= -github.com/cenkalti/backoff/v4 v4.1.3 h1:cFAlzYUlVYDysBEH2T5hyJZMh3+5+WCBvSnK6Q8UtC4= -github.com/cenkalti/backoff/v4 v4.1.3/go.mod h1:scbssz8iZGpm3xbr14ovlUdkxfGXNInqkPWOWmG2CLw= +github.com/cenkalti/backoff/v5 v5.0.3 h1:ZN+IMa753KfX5hd8vVaMixjnqRZ3y8CuJKRKj1xcsSM= +github.com/cenkalti/backoff/v5 v5.0.3/go.mod h1:rkhZdG3JZukswDf7f0cwqPNk4K0sa+F97BxZthm/crw= github.com/cockroachdb/errors v1.12.0 h1:d7oCs6vuIMUQRVbi6jWWWEJZahLCfJpnJSVobd1/sUo= github.com/cockroachdb/errors v1.12.0/go.mod h1:SvzfYNNBshAVbZ8wzNc/UPK3w1vf0dKDUP41ucAIf7g= github.com/cockroachdb/logtags v0.0.0-20230118201751-21c54148d20b h1:r6VH0faHjZeQy818SGhaone5OnYfxFR/+AzdY3sf5aE= @@ -44,19 +42,22 @@ github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ3 github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0 h1:NMZiJj8QnKe1LgsbDayM4UoHwbvwDRwnI3hwNaAHRnc= +github.com/decred/dcrd/dcrec/secp256k1/v4 v4.4.0/go.mod h1:ZXNYxsqcloTdSy/rNShjYzMhyjf0LaoftYK0p+A3h40= github.com/getsentry/sentry-go v0.27.0 h1:Pv98CIbtB3LkMWmXi4Joa5OOcwbmnX88sF5qbK3r3Ps= github.com/getsentry/sentry-go v0.27.0/go.mod h1:lc76E2QywIyW8WuBnwl8Lc4bkmQH4+w1gwTf25trprY= github.com/go-errors/errors v1.4.2 h1:J6MZopCL4uSllY1OfXM374weqZFFItUbrImctkmUxIA= github.com/go-errors/errors v1.4.2/go.mod h1:sIVyrIiJhuEF+Pj9Ebtd6P/rEYROXFi3BopGUQ5a5Og= -github.com/go-jose/go-jose/v3 v3.0.0 h1:s6rrhirfEP/CGIoc6p+PZAeogN2SxKav6Wp7+dyMWVo= -github.com/go-jose/go-jose/v3 v3.0.0/go.mod h1:RNkWWRld676jZEYoV3+XK8L2ZnNSvIsxFMht0mSX+u8= +github.com/go-jose/go-jose/v4 v4.1.3 h1:CVLmWDhDVRa6Mi/IgCgaopNosCaHz7zrMeF9MlZRkrs= +github.com/go-jose/go-jose/v4 v4.1.3/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08= 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/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= +github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2/go.mod h1:P1XiOD3dCwIKUDQYPy72D8LYyHL2YPYrpS2s69NZV8Q= github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= -github.com/google/go-cmp v0.5.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.5.2/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= @@ -64,6 +65,8 @@ github.com/google/go-github/v79 v79.0.0 h1:MdodQojuFPBhmtwHiBcIGLw/e/wei2PvFX9nd github.com/google/go-github/v79 v79.0.0/go.mod h1:OAFbNhq7fQwohojb06iIIQAB9CBGYLq999myfUFnrS4= github.com/google/go-querystring v1.1.0 h1:AnCroh3fv4ZBgVIf1Iwtovgjaw/GiKJo8M8yD/fhyJ8= github.com/google/go-querystring v1.1.0/go.mod h1:Kcdr2DB4koayq7X8pmAG4sNG59So17icRSOU623lUBU= +github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= +github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/gorilla/websocket v1.5.3 h1:saDtZ6Pbx/0u+bgYQ3q96pZgCzfhKXGPqt7kZ72aNNg= github.com/gorilla/websocket v1.5.3/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0= @@ -76,8 +79,24 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= -github.com/okta/okta-sdk-golang/v2 v2.20.0 h1:EDKM+uOPfihOMNwgHMdno+NAsIfyXkVnoFAYVPay0YU= -github.com/okta/okta-sdk-golang/v2 v2.20.0/go.mod h1:FMy5hN5G8Rd/VoS0XrfyPPhIfOVo78ZK7lvwiQRS2+U= +github.com/lestrrat-go/blackmagic v1.0.4 h1:IwQibdnf8l2KoO+qC3uT4OaTWsW7tuRQXy9TRN9QanA= +github.com/lestrrat-go/blackmagic v1.0.4/go.mod h1:6AWFyKNNj0zEXQYfTMPfZrAXUWUfTIZ5ECEUEJaijtw= +github.com/lestrrat-go/dsig v1.0.0 h1:OE09s2r9Z81kxzJYRn07TFM9XA4akrUdoMwr0L8xj38= +github.com/lestrrat-go/dsig v1.0.0/go.mod h1:dEgoOYYEJvW6XGbLasr8TFcAxoWrKlbQvmJgCR0qkDo= +github.com/lestrrat-go/dsig-secp256k1 v1.0.0 h1:JpDe4Aybfl0soBvoVwjqDbp+9S1Y2OM7gcrVVMFPOzY= +github.com/lestrrat-go/dsig-secp256k1 v1.0.0/go.mod h1:CxUgAhssb8FToqbL8NjSPoGQlnO4w3LG1P0qPWQm/NU= +github.com/lestrrat-go/httpcc v1.0.1 h1:ydWCStUeJLkpYyjLDHihupbn2tYmZ7m22BGkcvZZrIE= +github.com/lestrrat-go/httpcc v1.0.1/go.mod h1:qiltp3Mt56+55GPVCbTdM9MlqhvzyuL6W/NMDA8vA5E= +github.com/lestrrat-go/httprc/v3 v3.0.1 h1:3n7Es68YYGZb2Jf+k//llA4FTZMl3yCwIjFIk4ubevI= +github.com/lestrrat-go/httprc/v3 v3.0.1/go.mod h1:2uAvmbXE4Xq8kAUjVrZOq1tZVYYYs5iP62Cmtru00xk= +github.com/lestrrat-go/jwx/v3 v3.0.12 h1:p25r68Y4KrbBdYjIsQweYxq794CtGCzcrc5dGzJIRjg= +github.com/lestrrat-go/jwx/v3 v3.0.12/go.mod h1:HiUSaNmMLXgZ08OmGBaPVvoZQgJVOQphSrGr5zMamS8= +github.com/lestrrat-go/option v1.0.1 h1:oAzP2fvZGQKWkvHa1/SAcFolBEca1oN+mQ7eooNBEYU= +github.com/lestrrat-go/option v1.0.1/go.mod h1:5ZHFbivi4xwXxhxY9XHDe2FHo6/Z7WWmtT7T5nBBp3I= +github.com/lestrrat-go/option/v2 v2.0.0 h1:XxrcaJESE1fokHy3FpaQ/cXW8ZsIdWcdFzzLOcID3Ss= +github.com/lestrrat-go/option/v2 v2.0.0/go.mod h1:oSySsmzMoR0iRzCDCaUfsCzxQHUEuhOViQObyy7S6Vg= +github.com/okta/okta-sdk-golang/v6 v6.0.1 h1:i49fQkgTTSgVrXmLepwQy5wf99q9m8xMZ7b/DamyTcI= +github.com/okta/okta-sdk-golang/v6 v6.0.1/go.mod h1:EzV8yrIDarJfL8lAwUmfcuVQmaXe7gbhWyzKoCSb/WU= github.com/patrickmn/go-cache v2.1.0+incompatible h1:HRMgzkcYKYpi3C8ajMPV8OFXaaRUnok+kx1WdO15EQc= github.com/patrickmn/go-cache v2.1.0+incompatible/go.mod h1:3Qf8kWWT7OJRJbdiICTKqZju1ZixQ/KpMGzzAfe6+WQ= github.com/pingcap/errors v0.11.4 h1:lFuQV/oaUMGcD2tqt+01ROSmJs75VG1ToEOkZIZ4nE4= @@ -89,20 +108,24 @@ github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZb github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= github.com/rogpeppe/go-internal v1.9.0 h1:73kH8U+JUqXU8lRuOHeVHaa/SZPifC7BkcraZVejAe8= github.com/rogpeppe/go-internal v1.9.0/go.mod h1:WtVeX8xhTBvf0smdhujwtBcq4Qrzq/fJaraNFVN+nFs= +github.com/segmentio/asm v1.2.1 h1:DTNbBqs57ioxAD4PrArqftgypG4/qNpXoJx8TVXxPR0= +github.com/segmentio/asm v1.2.1/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs= github.com/slack-go/slack v0.17.3 h1:zV5qO3Q+WJAQ/XwbGfNFrRMaJ5T/naqaonyPV/1TP4g= github.com/slack-go/slack v0.17.3/go.mod h1:X+UqOufi3LYQHDnMG1vxf0J8asC6+WllXrVrhl8/Prk= github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= -github.com/stretchr/testify v1.10.0 h1:Xv5erBjTwe/5IxqUQTdXv5kgmIvbHo3QQyRwhJsOfJA= -github.com/stretchr/testify v1.10.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U= +github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U= +github.com/valyala/fastjson v1.6.4 h1:uAUNq9Z6ymTgGhcm0UynUAB6tlbakBrz6CQFax3BXVQ= +github.com/valyala/fastjson v1.6.4/go.mod h1:CLCAqky6SMuOcxStkYQvblddUtoRxhYMGLrsQns1aXY= github.com/yuin/goldmark v1.1.27/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= github.com/yuin/goldmark v1.2.1/go.mod h1:3hX8gzYuyVAZsxl0MRgGTJEmQBFcNTphYh9decYSb74= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= -golang.org/x/crypto v0.0.0-20190911031432-227b76d455e7/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= -golang.org/x/crypto v0.7.0 h1:AvwMYaRytfdeVt3u6mLaxYtErKYjxA2OXjJ1HHq6t3A= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= +golang.org/x/crypto v0.43.0 h1:dduJYIi3A3KOfdGOHX8AVZ/jGiyPa3IbBozJ5kNuE04= +golang.org/x/crypto v0.43.0/go.mod h1:BFbav4mRNlXJL4wNeejLpWxB7wMbc79PdRGhWKncxR0= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -117,12 +140,12 @@ golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= golang.org/x/sys v0.0.0-20200930185726-fdedc70b468f/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= -golang.org/x/sys v0.31.0 h1:ioabZlmFYtWhL+TRYpcnNlLwhyxaM9kWTDEmfnprqik= -golang.org/x/sys v0.31.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= +golang.org/x/sys v0.37.0 h1:fdNQudmxPjkdUTPnLn5mdQv7Zwvbvpaxqs831goi9kQ= +golang.org/x/sys v0.37.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= -golang.org/x/text v0.23.0 h1:D71I7dUrlY+VX0gQShAThNGHFxZ13dGLBHQLVl1mJlY= -golang.org/x/text v0.23.0/go.mod h1:/BLNzu4aZCJ1+kcD0DNRotWKage4q2rGVAg4o22unh4= +golang.org/x/text v0.30.0 h1:yznKA/E9zq54KzlzBEAWn1NXSQ8DIp/NYMy88xJjl4k= +golang.org/x/text v0.30.0/go.mod h1:yDdHFIX9t+tORqspjENWgzaCVXgk0yYnYuSZ8UzzBVM= golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roYkvgYkIh4xh/qjgUK9TdY2XT94GE= diff --git a/internal/okta/client.go b/internal/okta/client.go index 3adba78..d1f1adf 100644 --- a/internal/okta/client.go +++ b/internal/okta/client.go @@ -10,11 +10,11 @@ import ( "encoding/pem" "fmt" "net/http" + "net/url" "github.com/cockroachdb/errors" internalerrors "github.com/cruxstack/github-ops-app/internal/errors" - "github.com/okta/okta-sdk-golang/v2/okta" - "github.com/okta/okta-sdk-golang/v2/okta/query" + "github.com/okta/okta-sdk-golang/v6/okta" ) // DefaultScopes defines the required OAuth scopes for the Okta API. @@ -62,7 +62,7 @@ func convertToPKCS1(keyPEM []byte) ([]byte, error) { // Client wraps the Okta SDK client with custom configuration. type Client struct { - client *okta.Client + apiClient *okta.APIClient ctx context.Context githubUserField string } @@ -101,49 +101,76 @@ func NewClientWithContext(ctx context.Context, cfg *ClientConfig) (*Client, erro return nil, errors.Wrap(err, "failed to convert private key") } + scopes := cfg.Scopes + if len(scopes) == 0 { + scopes = DefaultScopes + } + + // v6 uses NewConfiguration which returns (config, error) opts := []okta.ConfigSetter{ okta.WithOrgUrl(orgURL), okta.WithAuthorizationMode("PrivateKey"), okta.WithClientId(cfg.ClientID), okta.WithPrivateKey(string(privateKey)), + okta.WithScopes(scopes), } if cfg.PrivateKeyID != "" { opts = append(opts, okta.WithPrivateKeyId(cfg.PrivateKeyID)) } - if len(cfg.Scopes) > 0 { - opts = append(opts, okta.WithScopes(cfg.Scopes)) - } else { - opts = append(opts, okta.WithScopes(DefaultScopes)) - } - if certPool, ok := ctx.Value("okta_tls_cert_pool").(*x509.CertPool); ok && certPool != nil { - httpClient := http.Client{ + httpClient := &http.Client{ Transport: &http.Transport{ TLSClientConfig: &tls.Config{ RootCAs: certPool, }, }, } - opts = append(opts, okta.WithHttpClient(httpClient)) + opts = append(opts, okta.WithHttpClientPtr(httpClient)) } - _, client, err := okta.NewClient(ctx, opts...) + oktaCfg, err := okta.NewConfiguration(opts...) if err != nil { - return nil, errors.Wrap(err, "failed to create okta client") + return nil, errors.Wrap(err, "failed to create okta configuration") + } + + // v6 SDK parses OrgUrl and uses url.Parse().Hostname() which strips the port + // for testing with custom ports, we need to override the server configuration + // the SDK uses Servers[0].URL for building API call URLs + if cfg.BaseURL != "" { + parsedURL, err := url.Parse(cfg.BaseURL) + if err == nil { + // Override the default server configuration with full URL including port + oktaCfg.Servers = okta.ServerConfigurations{ + okta.ServerConfiguration{ + URL: cfg.BaseURL, + Description: "Custom Okta server (test mode)", + }, + } + // Also update Host and Scheme for consistency + // Include port in Host if present + if parsedURL.Port() != "" { + oktaCfg.Host = parsedURL.Host // Host includes port + } else { + oktaCfg.Host = parsedURL.Hostname() + } + oktaCfg.Scheme = parsedURL.Scheme + } } + apiClient := okta.NewAPIClient(oktaCfg) + return &Client{ - client: client, + apiClient: apiClient, ctx: ctx, githubUserField: cfg.GitHubUserField, }, nil } -// GetClient returns the underlying Okta SDK client. -func (c *Client) GetClient() *okta.Client { - return c.client +// GetAPIClient returns the underlying Okta SDK API client. +func (c *Client) GetAPIClient() *okta.APIClient { + return c.apiClient } // GetContext returns the context used for API requests. @@ -152,8 +179,8 @@ func (c *Client) GetContext() context.Context { } // ListGroups fetches all Okta groups. -func (c *Client) ListGroups() ([]*okta.Group, error) { - groups, _, err := c.client.Group.ListGroups(c.ctx, &query.Params{}) +func (c *Client) ListGroups() ([]okta.Group, error) { + groups, _, err := c.apiClient.GroupAPI.ListGroups(c.ctx).Execute() if err != nil { return nil, errors.Wrap(err, "failed to list groups") } @@ -162,16 +189,32 @@ func (c *Client) ListGroups() ([]*okta.Group, error) { // GetGroupByName searches for an Okta group by exact name match. func (c *Client) GetGroupByName(name string) (*okta.Group, error) { - groups, _, err := c.client.Group.ListGroups(c.ctx, &query.Params{ - Q: name, - }) + groups, _, err := c.apiClient.GroupAPI.ListGroups(c.ctx).Q(name).Execute() if err != nil { return nil, errors.Wrapf(err, "failed to search for group '%s'", name) } - for _, group := range groups { - if group.Profile.Name == name { - return group, nil + for i := range groups { + group := &groups[i] + // check if profile is nil + if group.Profile == nil { + continue + } + + // try OktaUserGroupProfile first + if group.Profile.OktaUserGroupProfile != nil { + groupName := group.Profile.OktaUserGroupProfile.GetName() + if groupName == name { + return group, nil + } + } + + // try OktaActiveDirectoryGroupProfile as fallback + if group.Profile.OktaActiveDirectoryGroupProfile != nil { + groupName := group.Profile.OktaActiveDirectoryGroupProfile.GetName() + if groupName == name { + return group, nil + } } } @@ -189,7 +232,7 @@ type GroupMembersResult struct { // suspended/deprovisioned users. skips users without a GitHub username in // their profile and tracks them separately. func (c *Client) GetGroupMembers(groupID string) (*GroupMembersResult, error) { - users, _, err := c.client.Group.ListGroupUsers(c.ctx, groupID, &query.Params{}) + users, _, err := c.apiClient.GroupAPI.ListGroupUsers(c.ctx, groupID).Execute() if err != nil { return nil, errors.Wrapf(err, "failed to list members for group '%s'", groupID) } @@ -200,24 +243,32 @@ func (c *Client) GetGroupMembers(groupID string) (*GroupMembersResult, error) { } for _, user := range users { - if user.Status != "ACTIVE" { + if user.GetStatus() != "ACTIVE" { continue } - if user.Profile == nil { + profile := user.GetProfile() + additionalProps := profile.AdditionalProperties + if additionalProps == nil { continue } - githubUsername := (*user.Profile)[c.githubUserField] - if username, ok := githubUsername.(string); ok && username != "" { - result.Members = append(result.Members, username) - } else { - email := (*user.Profile)["email"] - if emailStr, ok := email.(string); ok && emailStr != "" { - result.SkippedNoGitHubUsername = append( - result.SkippedNoGitHubUsername, emailStr) + githubUsername, ok := additionalProps[c.githubUserField] + if ok { + if username, ok := githubUsername.(string); ok && username != "" { + result.Members = append(result.Members, username) + continue } } + + // user doesn't have github username, track by email + if email, ok := additionalProps["email"].(string); ok && email != "" { + result.SkippedNoGitHubUsername = append( + result.SkippedNoGitHubUsername, email) + } else if profile.GetEmail() != "" { + result.SkippedNoGitHubUsername = append( + result.SkippedNoGitHubUsername, profile.GetEmail()) + } } return result, nil diff --git a/internal/okta/groups.go b/internal/okta/groups.go index c97aa65..cd3e84a 100644 --- a/internal/okta/groups.go +++ b/internal/okta/groups.go @@ -5,7 +5,7 @@ import ( "github.com/cockroachdb/errors" internalerrors "github.com/cruxstack/github-ops-app/internal/errors" - "github.com/okta/okta-sdk-golang/v2/okta" + "github.com/okta/okta-sdk-golang/v6/okta" ) // GroupInfo contains Okta group details and member list. @@ -34,19 +34,31 @@ func (c *Client) GetGroupsByPattern(pattern string) ([]*GroupInfo, error) { var matched []*GroupInfo for _, group := range allGroups { - if group == nil || group.Profile == nil { + if group.Profile == nil { continue } - if re.MatchString(group.Profile.Name) { - result, err := c.GetGroupMembers(group.Id) + // extract group name from either profile type + var groupName string + if group.Profile.OktaUserGroupProfile != nil { + groupName = group.Profile.OktaUserGroupProfile.GetName() + } else if group.Profile.OktaActiveDirectoryGroupProfile != nil { + groupName = group.Profile.OktaActiveDirectoryGroupProfile.GetName() + } + + if groupName == "" { + continue + } + + if re.MatchString(groupName) { + result, err := c.GetGroupMembers(group.GetId()) if err != nil { continue } matched = append(matched, &GroupInfo{ - ID: group.Id, - Name: group.Profile.Name, + ID: group.GetId(), + Name: groupName, Members: result.Members, SkippedNoGitHubUsername: result.SkippedNoGitHubUsername, }) @@ -63,14 +75,24 @@ func (c *Client) GetGroupInfo(groupName string) (*GroupInfo, error) { return nil, err } - result, err := c.GetGroupMembers(group.Id) + result, err := c.GetGroupMembers(group.GetId()) if err != nil { return nil, err } + // extract group name from either profile type + var name string + if group.Profile != nil { + if group.Profile.OktaUserGroupProfile != nil { + name = group.Profile.OktaUserGroupProfile.GetName() + } else if group.Profile.OktaActiveDirectoryGroupProfile != nil { + name = group.Profile.OktaActiveDirectoryGroupProfile.GetName() + } + } + return &GroupInfo{ - ID: group.Id, - Name: group.Profile.Name, + ID: group.GetId(), + Name: name, Members: result.Members, SkippedNoGitHubUsername: result.SkippedNoGitHubUsername, }, nil @@ -78,7 +100,7 @@ func (c *Client) GetGroupInfo(groupName string) (*GroupInfo, error) { // FilterEnabledGroups filters Okta groups to only those in the enabled list. // returns all groups if enabled list is empty. -func FilterEnabledGroups(groups []*okta.Group, enabledNames []string) []*okta.Group { +func FilterEnabledGroups(groups []okta.Group, enabledNames []string) []okta.Group { if len(enabledNames) == 0 { return groups } @@ -88,10 +110,19 @@ func FilterEnabledGroups(groups []*okta.Group, enabledNames []string) []*okta.Gr enabledMap[name] = true } - var filtered []*okta.Group + var filtered []okta.Group for _, group := range groups { - if enabledMap[group.Profile.Name] { - filtered = append(filtered, group) + if group.Profile != nil { + var groupName string + if group.Profile.OktaUserGroupProfile != nil { + groupName = group.Profile.OktaUserGroupProfile.GetName() + } else if group.Profile.OktaActiveDirectoryGroupProfile != nil { + groupName = group.Profile.OktaActiveDirectoryGroupProfile.GetName() + } + + if groupName != "" && enabledMap[groupName] { + filtered = append(filtered, group) + } } } From 2ee348b29eee00ad592124750a339432d37d98c3 Mon Sep 17 00:00:00 2001 From: Brian Ojeda <9335829+sgtoj@users.noreply.github.com> Date: Mon, 8 Dec 2025 09:08:14 -0500 Subject: [PATCH 2/2] docs: switch to ascii for diagram --- README.md | 30 +++++++++++++++--------------- 1 file changed, 15 insertions(+), 15 deletions(-) diff --git a/README.md b/README.md index 2559646..8996877 100644 --- a/README.md +++ b/README.md @@ -214,21 +214,21 @@ CMD ["/server"] ## How It Works ``` -┌────────────┐ ┌─────────────────────────────────────┐ ┌────────────┐ -│ GitHub │────▶│ github-ops-app │────▶│ Slack │ -│ webhooks │ │ │ │ alerts │ -└────────────┘ │ ┌───────────────────────────────┐ │ └────────────┘ - │ │ PR Compliance Check │ │ -┌────────────┐ │ │ • Verify branch protection │ │ ┌────────────┐ -│ Okta │────▶│ │ • Detect bypasses │ │────▶│ GitHub │ -│ groups │ │ └───────────────────────────────┘ │ │ Teams API │ -└────────────┘ │ │ └────────────┘ - │ ┌───────────────────────────────┐ │ - │ │ Okta Sync Engine │ │ - │ │ • Map groups to teams │ │ - │ │ • Sync membership │ │ - │ └───────────────────────────────┘ │ - └─────────────────────────────────────┘ ++------------+ +------------------------------------+ +-----------+ +| GitHub | ---> | github-ops-app | ---> | Slack | +| webhooks | | | | alerts | ++------------+ | +------------------------------+ | +-----------+ + | | PR Compliance Check | | ++------------+ | | - verify branch protection | | +-----------+ +| Okta | ---> | | - detect bypasses | | ---> | GitHub | +| groups | | +------------------------------+ | | Teams API | ++------------+ | | +-----------+ + | +------------------------------+ | + | | Okta Sync Engine | | + | | - map groups to teams | | + | | - sync membership | | + | +------------------------------+ | + +------------------------------------+ ``` ### Okta Sync Flow