Skip to content

Action-revoke-user-sessions#67

Open
cvkramer wants to merge 1 commit into
mainfrom
action-revoke-user-sessions
Open

Action-revoke-user-sessions#67
cvkramer wants to merge 1 commit into
mainfrom
action-revoke-user-sessions

Conversation

@cvkramer
Copy link
Copy Markdown
Contributor

@cvkramer cvkramer commented Jan 25, 2026

Implement action to revoke all active user sessions (web and device)
using Google Admin Directory API. Includes comprehensive tests,
and handler implementation.

Changes:

  • Add revokeUserSessionsHandler in pkg/connector/actions.go
  • Define revokeUserSessionsActionSchema in pkg/connector/connector.go
  • Add comprehensive test suite (4 test cases covering success, validation,
    error handling, and non-idempotent behavior)

Technical details:

  • Requires admin.directory.user.security OAuth scope
  • Uses ACTION_TYPE_ACCOUNT for UI categorization
  • Not idempotent (Google API has no session state check)
  • Input validation: required user_email with format checking
  • Returns success status and user email on completion

Summary by CodeRabbit

  • New Features
    • Added ability to revoke all active sessions for a specified user email across web and device platforms, with input validation and clear success responses.
  • Tests
    • Added comprehensive tests covering successful revocation, validation errors, user-not-found behavior, and repeated revocations to ensure correct API interactions.

✏️ Tip: You can customize this high-level summary in your review settings.

Implement action to revoke all active user sessions (web and device)
using Google Admin Directory API. Includes comprehensive tests,
and handler implementation.

Changes:
- Add revokeUserSessionsHandler in pkg/connector/actions.go
- Define revokeUserSessionsActionSchema in pkg/connector/connector.go
- Add comprehensive test suite (4 test cases covering success, validation,
  error handling, and non-idempotent behavior)

Technical details:
- Requires admin.directory.user.security OAuth scope
- Uses ACTION_TYPE_ACCOUNT for UI categorization
- Not idempotent (Google API has no session state check)
- Input validation: required user_email with format checking
- Returns success status and user email on completion
@cvkramer cvkramer requested a review from a team January 25, 2026 21:03
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Jan 25, 2026

Walkthrough

A new "revoke user sessions" action is added to the Google Workspace connector: it validates a provided user_email, calls the Directory service SignOut API for that user, and returns a success response; tests and action schema registration are included.

Changes

Cohort / File(s) Summary
Core Implementation
pkg/connector/actions.go
Adds revokeUserSessionsHandler on GoogleWorkspace to validate user_email, obtain Directory service with AdminDirectoryUserSecurityScope, call SignOut for the user, and return { success: true, user_email }.
Test Coverage
pkg/connector/actions_test.go
Adds mock POST /admin/directory/v1/users/{userKey}/signOut handling, tracks SignedOut and signOutCount, caches AdminDirectoryUserSecurityScope, and adds tests: success, validation errors, user not found, and non-idempotency.
Schema Registration
pkg/connector/connector.go
Registers new revoke_user_sessions action schema (required user_email, returns success and user_email) and adds it to GlobalActions.

Sequence Diagram

sequenceDiagram
    participant Client
    participant Handler as revokeUserSessionsHandler
    participant Service as DirectoryService
    participant API as GoogleAPI_SignOut

    Client->>Handler: invoke action with user_email
    Handler->>Handler: validate user_email
    Handler->>Service: Get admin Directory service (AdminDirectoryUserSecurityScope)
    Service-->>Handler: directory service instance
    Handler->>API: POST /users/{userKey}/signOut
    API-->>Handler: 204 No Content / error
    Handler->>Client: return { success: true, user_email } / error
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐰 I hopped through code to snap a ban,
Signed out sessions with a steady hand,
Tests kept watch, the server sighed,
Emails checked, no faults to hide,
A little rabbit's job well-planned.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The PR title 'Action-revoke-user-sessions' is directly related to the main change, which adds a new action handler for revoking user sessions in Google Workspace.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@pkg/connector/actions_test.go`:
- Around line 46-72: Run goimports (or gofmt) on the test file to fix
formatting/import ordering issues flagged by static analysis; format the file
containing the mux.HandleFunc(...) test handler (the function manipulating
state.mtx, state.signOutCount and setting u.SignedOut and writing
http.StatusNoContent) so imports are organized and the file passes
goimports/gofmt checks, then commit the formatted file.

Comment on lines 46 to +72
mux.HandleFunc("/admin/directory/v1/users/", func(w http.ResponseWriter, r *http.Request) {
// path suffix is userKey
userKey := strings.TrimPrefix(r.URL.Path, "/admin/directory/v1/users/")
// path suffix is userKey, may have /signOut suffix
path := strings.TrimPrefix(r.URL.Path, "/admin/directory/v1/users/")

// Check for signOut endpoint
if strings.HasSuffix(path, "/signOut") {
userKey := strings.TrimSuffix(path, "/signOut")
state.mtx.Lock()
defer state.mtx.Unlock()

if r.Method == http.MethodPost {
state.signOutCount++
u := state.users[userKey]
if u == nil {
http.Error(w, "not found", http.StatusNotFound)
return
}
u.SignedOut = true
w.WriteHeader(http.StatusNoContent) // SignOut returns 204 No Content
return
}
http.Error(w, "method not allowed", http.StatusMethodNotAllowed)
return
}

// Regular user operations
userKey := path
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Fix formatting issue flagged by static analysis.

The static analysis tool (goimports) indicates this file is not properly formatted. Please run goimports or gofmt to fix the formatting.

🔧 Suggested fix

Run the following command to fix:

goimports -w pkg/connector/actions_test.go

Or if using gofmt:

gofmt -w pkg/connector/actions_test.go
🧰 Tools
🪛 GitHub Check: go-lint

[failure] 49-49:
File is not properly formatted (goimports)

🤖 Prompt for AI Agents
In `@pkg/connector/actions_test.go` around lines 46 - 72, Run goimports (or gofmt)
on the test file to fix formatting/import ordering issues flagged by static
analysis; format the file containing the mux.HandleFunc(...) test handler (the
function manipulating state.mtx, state.signOutCount and setting u.SignedOut and
writing http.StatusNoContent) so imports are organized and the file passes
goimports/gofmt checks, then commit the formatted file.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@pkg/connector/actions_test.go`:
- Around line 421-429: The mutex (state.mtx) is locked then t.Fatalf is called
on failure which can leave the mutex locked; change the pattern in the
verification blocks (where state.mtx.Lock() is used before checking
state.signOutCount and state.users["alice@example.com"].SignedOut, and the
similar block around sign-in checks) to immediately defer state.mtx.Unlock()
after acquiring the lock so the mutex is always released even if t.Fatalf exits
the goroutine; keep the same assertions (state.signOutCount and state.users[...]
.SignedOut) but ensure the defer is placed right after calling state.mtx.Lock().

Comment on lines +421 to +429
// Verify API was called
state.mtx.Lock()
if state.signOutCount != 1 {
t.Fatalf("expected one signOut call, got: %d", state.signOutCount)
}
if !state.users["alice@example.com"].SignedOut {
t.Fatalf("expected user to be signed out")
}
state.mtx.Unlock()
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

Avoid leaving the mutex locked on test failure.

Fatalf exits the goroutine; without a deferred unlock, the lock can remain held and cause test teardown hangs.

🔧 Suggested fix
 	// Verify API was called
 	state.mtx.Lock()
+	defer state.mtx.Unlock()
 	if state.signOutCount != 1 {
 		t.Fatalf("expected one signOut call, got: %d", state.signOutCount)
 	}
 	if !state.users["alice@example.com"].SignedOut {
 		t.Fatalf("expected user to be signed out")
 	}
-	state.mtx.Unlock()

@@
-	state.mtx.Lock()
-	if state.signOutCount != firstCallCount+1 {
-		t.Fatalf("expected another revoke call (not idempotent), got %d total calls", state.signOutCount)
-	}
-	state.mtx.Unlock()
+	func() {
+		state.mtx.Lock()
+		defer state.mtx.Unlock()
+		if state.signOutCount != firstCallCount+1 {
+			t.Fatalf("expected another revoke call (not idempotent), got %d total calls", state.signOutCount)
+		}
+	}()

Also applies to: 534-538

🤖 Prompt for AI Agents
In `@pkg/connector/actions_test.go` around lines 421 - 429, The mutex (state.mtx)
is locked then t.Fatalf is called on failure which can leave the mutex locked;
change the pattern in the verification blocks (where state.mtx.Lock() is used
before checking state.signOutCount and
state.users["alice@example.com"].SignedOut, and the similar block around sign-in
checks) to immediately defer state.mtx.Unlock() after acquiring the lock so the
mutex is always released even if t.Fatalf exits the goroutine; keep the same
assertions (state.signOutCount and state.users[...] .SignedOut) but ensure the
defer is placed right after calling state.mtx.Lock().

@cvkramer cvkramer force-pushed the action-revoke-user-sessions branch from b8ba591 to 4f0e9b9 Compare January 25, 2026 22:46
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.

2 participants