Remove deadcode

Signed-off-by: Bryan Frimin <bryan@getprobo.com>
This commit is contained in:
Bryan Frimin
2026-03-13 16:50:35 +01:00
parent 7fd4221199
commit ef76a8d2e1
76 changed files with 137 additions and 4424 deletions

View File

@@ -49,11 +49,6 @@ type (
InvitationToken string
}
LoadOrCreateIdentityRequest struct {
Email mail.Addr
FullName string
}
CreateIdentityWithPasswordRequest struct {
Email mail.Addr
Password string
@@ -115,14 +110,6 @@ func (req ChangePasswordRequest) Validate() error {
return v.Error()
}
func (req LoadOrCreateIdentityRequest) Validate() error {
v := validator.New()
v.Check(req.FullName, "fullName", validator.NotEmpty(), validator.MinLen(1), validator.MaxLen(255))
return v.Error()
}
func (req CreateIdentityWithPasswordRequest) Validate() error {
v := validator.New()

View File

@@ -313,19 +313,6 @@ func (e ErrInvalidCredentials) Error() string {
return e.message
}
type ErrInvitationNotDeleted struct {
InvitationID gid.GID
Status string
}
func NewInvitationNotDeletedError(invitationID gid.GID, status string) error {
return &ErrInvitationNotDeleted{InvitationID: invitationID, Status: status}
}
func (e ErrInvitationNotDeleted) Error() string {
return fmt.Sprintf("cannot delete invitation %q in %q status", e.InvitationID, e.Status)
}
type ErrPasswordAuthenticationRequired struct {
Reason string
}

View File

@@ -1,126 +0,0 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package policy
import (
"fmt"
"strings"
)
// Action represents a permission action in the format "service:resource:operation"
// Examples: "iam:identity:get", "documents:document:write", "risks:risk:delete"
type Action string
// ActionDefinition provides metadata about an action for documentation and validation.
type ActionDefinition struct {
Action Action
Service string // e.g., "iam", "documents", "risks"
Resource string // e.g., "identity", "document", "risk"
Operation string // e.g., "get", "list", "create", "update", "delete"
Description string
}
// ActionRegistry holds all registered actions and provides lookup/validation.
type ActionRegistry struct {
actions map[Action]ActionDefinition
}
// NewActionRegistry creates a new empty action registry.
func NewActionRegistry() *ActionRegistry {
return &ActionRegistry{
actions: make(map[Action]ActionDefinition),
}
}
// Register adds an action definition to the registry.
// Returns an error if the action is already registered.
func (r *ActionRegistry) Register(def ActionDefinition) error {
if _, exists := r.actions[def.Action]; exists {
return fmt.Errorf("action %q already registered", def.Action)
}
// Validate action format
if err := validateActionFormat(def.Action); err != nil {
return fmt.Errorf("invalid action format: %w", err)
}
r.actions[def.Action] = def
return nil
}
// MustRegister is like Register but panics on error.
// Useful for setting up registries in application startup.
func (r *ActionRegistry) MustRegister(def ActionDefinition) {
if err := r.Register(def); err != nil {
panic(err)
}
}
// Get returns the definition for an action, or false if not found.
func (r *ActionRegistry) Get(action Action) (ActionDefinition, bool) {
def, ok := r.actions[action]
return def, ok
}
// Exists checks if an action is registered.
func (r *ActionRegistry) Exists(action Action) bool {
_, ok := r.actions[action]
return ok
}
// All returns all registered action definitions.
func (r *ActionRegistry) All() []ActionDefinition {
result := make([]ActionDefinition, 0, len(r.actions))
for _, def := range r.actions {
result = append(result, def)
}
return result
}
// ByService returns all actions for a given service.
func (r *ActionRegistry) ByService(service string) []ActionDefinition {
var result []ActionDefinition
for _, def := range r.actions {
if def.Service == service {
result = append(result, def)
}
}
return result
}
// validateActionFormat ensures action follows "service:resource:operation" format.
func validateActionFormat(action Action) error {
parts := strings.Split(string(action), ":")
if len(parts) != 3 {
return fmt.Errorf("action must have format 'service:resource:operation', got %q", action)
}
for i, part := range parts {
if part == "" {
return fmt.Errorf("action part %d is empty in %q", i, action)
}
}
return nil
}
// ParseAction extracts service, resource, and operation from an action string.
func ParseAction(action Action) (service, resource, operation string, err error) {
parts := strings.Split(string(action), ":")
if len(parts) != 3 {
return "", "", "", fmt.Errorf("invalid action format: %q", action)
}
return parts[0], parts[1], parts[2], nil
}

View File

@@ -1,219 +0,0 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package policy
import (
"testing"
)
func TestActionRegistry_Register(t *testing.T) {
tests := []struct {
name string
def ActionDefinition
wantErr bool
}{
{
name: "valid action",
def: ActionDefinition{
Action: "iam:identity:get",
Service: "iam",
Resource: "identity",
Operation: "get",
Description: "Get identity",
},
wantErr: false,
},
{
name: "invalid format - missing parts",
def: ActionDefinition{
Action: "iam:identity",
Service: "iam",
},
wantErr: true,
},
{
name: "invalid format - empty part",
def: ActionDefinition{
Action: "iam::get",
Service: "iam",
},
wantErr: true,
},
{
name: "invalid format - too many parts",
def: ActionDefinition{
Action: "iam:identity:get:extra",
Service: "iam",
},
wantErr: true,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
r := NewActionRegistry()
err := r.Register(tt.def)
if (err != nil) != tt.wantErr {
t.Errorf("Register() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestActionRegistry_DuplicateRegistration(t *testing.T) {
r := NewActionRegistry()
def := ActionDefinition{
Action: "iam:identity:get",
Service: "iam",
Resource: "identity",
Operation: "get",
Description: "Get identity",
}
// First registration should succeed
if err := r.Register(def); err != nil {
t.Fatalf("First registration failed: %v", err)
}
// Second registration should fail
if err := r.Register(def); err == nil {
t.Error("Expected error for duplicate registration, got nil")
}
}
func TestActionRegistry_Get(t *testing.T) {
r := NewActionRegistry()
def := ActionDefinition{
Action: "iam:identity:get",
Service: "iam",
Resource: "identity",
Operation: "get",
Description: "Get identity",
}
r.MustRegister(def)
// Get existing action
got, ok := r.Get("iam:identity:get")
if !ok {
t.Error("Expected to find action")
}
if got.Action != def.Action {
t.Errorf("Got action %v, want %v", got.Action, def.Action)
}
// Get non-existing action
_, ok = r.Get("iam:identity:delete")
if ok {
t.Error("Expected not to find action")
}
}
func TestActionRegistry_Exists(t *testing.T) {
r := NewActionRegistry()
r.MustRegister(ActionDefinition{
Action: "iam:identity:get",
Service: "iam",
Resource: "identity",
Operation: "get",
})
if !r.Exists("iam:identity:get") {
t.Error("Expected action to exist")
}
if r.Exists("iam:identity:delete") {
t.Error("Expected action not to exist")
}
}
func TestActionRegistry_ByService(t *testing.T) {
r := NewActionRegistry()
r.MustRegister(ActionDefinition{Action: "iam:identity:get", Service: "iam", Resource: "identity", Operation: "get"})
r.MustRegister(ActionDefinition{Action: "iam:identity:update", Service: "iam", Resource: "identity", Operation: "update"})
r.MustRegister(ActionDefinition{Action: "documents:document:read", Service: "documents", Resource: "document", Operation: "read"})
iamActions := r.ByService("iam")
if len(iamActions) != 2 {
t.Errorf("Expected 2 IAM actions, got %d", len(iamActions))
}
docActions := r.ByService("documents")
if len(docActions) != 1 {
t.Errorf("Expected 1 documents action, got %d", len(docActions))
}
unknownActions := r.ByService("unknown")
if len(unknownActions) != 0 {
t.Errorf("Expected 0 unknown actions, got %d", len(unknownActions))
}
}
func TestParseAction(t *testing.T) {
tests := []struct {
action Action
wantSvc string
wantRes string
wantOp string
wantErr bool
}{
{
action: "iam:identity:get",
wantSvc: "iam",
wantRes: "identity",
wantOp: "get",
wantErr: false,
},
{
action: "documents:document:read",
wantSvc: "documents",
wantRes: "document",
wantOp: "read",
wantErr: false,
},
{
action: "invalid",
wantErr: true,
},
{
action: "invalid:action",
wantErr: true,
},
}
for _, tt := range tests {
t.Run(string(tt.action), func(t *testing.T) {
svc, res, op, err := ParseAction(tt.action)
if (err != nil) != tt.wantErr {
t.Errorf("ParseAction() error = %v, wantErr %v", err, tt.wantErr)
return
}
if !tt.wantErr {
if svc != tt.wantSvc {
t.Errorf("service = %v, want %v", svc, tt.wantSvc)
}
if res != tt.wantRes {
t.Errorf("resource = %v, want %v", res, tt.wantRes)
}
if op != tt.wantOp {
t.Errorf("operation = %v, want %v", op, tt.wantOp)
}
}
})
}
}

View File

@@ -1,159 +0,0 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package policy
import (
"errors"
"fmt"
"maps"
"go.probo.inc/probo/pkg/gid"
)
var (
// ErrAccessDenied is returned when access is explicitly denied.
ErrAccessDenied = errors.New("access denied")
// ErrNoMatchingPolicy is returned when no policy grants access (implicit deny).
ErrNoMatchingPolicy = errors.New("no matching policy")
)
// AccessDeniedError provides detailed information about why access was denied.
type AccessDeniedError struct {
Principal gid.GID
Resource gid.GID
Action string
Reason string
Statement *Statement // The statement that denied access (if explicit deny)
}
func (e *AccessDeniedError) Error() string {
if e.Statement != nil && e.Statement.SID != "" {
return fmt.Sprintf("access denied: principal %s cannot perform %s on %s (denied by %s)",
e.Principal, e.Action, e.Resource, e.Statement.SID)
}
return fmt.Sprintf("access denied: principal %s cannot perform %s on %s: %s",
e.Principal, e.Action, e.Resource, e.Reason)
}
func (e *AccessDeniedError) Unwrap() error {
return ErrAccessDenied
}
// Authorizer evaluates policies to authorize actions.
type Authorizer struct {
evaluator *Evaluator
registry *ActionRegistry
}
// NewAuthorizer creates a new authorizer with the given action registry.
func NewAuthorizer(registry *ActionRegistry) *Authorizer {
return &Authorizer{
evaluator: NewEvaluator(),
registry: registry,
}
}
// AuthorizeParams contains all parameters for an authorization check.
type AuthorizeParams struct {
// Principal is the actor requesting access.
Principal gid.GID
// Resource is the target resource.
Resource gid.GID
// Action is the operation being performed.
Action string
// Policies are the policies to evaluate (typically role-based + self-manage).
Policies []*Policy
// ResourceAttributes provides additional attributes about the resource
// for condition evaluation (e.g., owner_id, tenant_id).
ResourceAttributes map[string]string
}
// Authorize checks if the action is allowed based on the provided policies.
// Returns nil if allowed, or an error describing why access was denied.
func (a *Authorizer) Authorize(params AuthorizeParams) error {
// Validate action exists in registry (optional - can be disabled for flexibility)
if a.registry != nil && !a.registry.Exists(Action(params.Action)) {
return &AccessDeniedError{
Principal: params.Principal,
Resource: params.Resource,
Action: params.Action,
Reason: "unknown action",
}
}
// Build condition context
conditionCtx := ConditionContext{
Principal: map[string]string{
"id": params.Principal.String(),
},
Resource: map[string]string{
"id": params.Resource.String(),
},
}
// Add resource attributes to context
maps.Copy(conditionCtx.Resource, params.ResourceAttributes)
// Build authorization request
req := AuthorizationRequest{
Principal: params.Principal,
Resource: params.Resource,
Action: params.Action,
ConditionContext: conditionCtx,
}
// Evaluate policies
result := a.evaluator.Evaluate(req, params.Policies)
switch result.Decision {
case DecisionAllow:
return nil
case DecisionDeny:
return &AccessDeniedError{
Principal: params.Principal,
Resource: params.Resource,
Action: params.Action,
Reason: "explicitly denied",
Statement: result.MatchedStatement,
}
case DecisionNoMatch:
return &AccessDeniedError{
Principal: params.Principal,
Resource: params.Resource,
Action: params.Action,
Reason: "no policy allows this action",
}
default:
return &AccessDeniedError{
Principal: params.Principal,
Resource: params.Resource,
Action: params.Action,
Reason: "unexpected evaluation result",
}
}
}
// IsAllowed is a convenience method that returns true if access is allowed.
func (a *Authorizer) IsAllowed(params AuthorizeParams) bool {
return a.Authorize(params) == nil
}

View File

@@ -1,277 +0,0 @@
// Copyright (c) 2025 Probo Inc <hello@getprobo.com>.
//
// Permission to use, copy, modify, and/or distribute this software for any
// purpose with or without fee is hereby granted, provided that the above
// copyright notice and this permission notice appear in all copies.
//
// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH
// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
// AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT,
// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR
// OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
// PERFORMANCE OF THIS SOFTWARE.
package policy
import (
"errors"
"testing"
)
func TestAuthorizer_Authorize(t *testing.T) {
// Create a simple registry for testing
registry := NewActionRegistry()
registry.MustRegister(ActionDefinition{
Action: "test:resource:get",
Service: "test",
Resource: "resource",
Operation: "get",
Description: "Get resource",
})
registry.MustRegister(ActionDefinition{
Action: "test:resource:update",
Service: "test",
Resource: "resource",
Operation: "update",
Description: "Update resource",
})
registry.MustRegister(ActionDefinition{
Action: "test:resource:delete",
Service: "test",
Resource: "resource",
Operation: "delete",
Description: "Delete resource",
})
registry.MustRegister(ActionDefinition{
Action: "test:other:get",
Service: "test",
Resource: "other",
Operation: "get",
Description: "Get other",
})
authorizer := NewAuthorizer(registry)
policies := []*Policy{
NewPolicy("test", "Test",
Allow("test:resource:get", "test:resource:update"),
Deny("test:resource:delete"),
),
}
tests := []struct {
name string
action string
wantErr bool
errType error
}{
{
name: "allowed action",
action: "test:resource:get",
wantErr: false,
},
{
name: "denied action",
action: "test:resource:delete",
wantErr: true,
errType: ErrAccessDenied,
},
{
name: "no matching policy",
action: "test:other:get",
wantErr: true,
errType: ErrAccessDenied,
},
{
name: "unknown action",
action: "unknown:action:here",
wantErr: true,
errType: ErrAccessDenied,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := authorizer.Authorize(AuthorizeParams{
Action: tt.action,
Policies: policies,
ResourceAttributes: map[string]string{
"id": "res_123",
},
})
if (err != nil) != tt.wantErr {
t.Errorf("Authorize() error = %v, wantErr %v", err, tt.wantErr)
return
}
if tt.wantErr && tt.errType != nil {
if !errors.Is(err, tt.errType) {
t.Errorf("Authorize() error type = %T, want %T", err, tt.errType)
}
}
})
}
}
func TestAuthorizer_Authorize_WithConditions(t *testing.T) {
authorizer := NewAuthorizer(nil)
// Self-manage policy for testing
selfManagePolicy := NewPolicy("self-manage", "Self Manage",
Allow("test:identity:get", "test:identity:update").
When(Equals("principal.id", "resource.id")),
)
tests := []struct {
name string
action string
resourceAttributes map[string]string
wantErr bool
}{
{
name: "condition not satisfied - GID won't match string",
action: "test:identity:get",
resourceAttributes: map[string]string{
"id": "user_123",
},
wantErr: true, // Will fail because principal GID won't match string
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
err := authorizer.Authorize(AuthorizeParams{
Action: tt.action,
Policies: []*Policy{selfManagePolicy},
ResourceAttributes: tt.resourceAttributes,
})
if (err != nil) != tt.wantErr {
t.Errorf("Authorize() error = %v, wantErr %v", err, tt.wantErr)
}
})
}
}
func TestAuthorizer_Authorize_WithoutRegistry(t *testing.T) {
// Authorizer without registry should not validate actions
authorizer := NewAuthorizer(nil)
policies := []*Policy{
NewPolicy("test", "Test", Allow("custom:action:here")),
}
err := authorizer.Authorize(AuthorizeParams{
Action: "custom:action:here",
Policies: policies,
})
if err != nil {
t.Errorf("Expected no error for custom action without registry, got %v", err)
}
}
func TestAuthorizer_IsAllowed(t *testing.T) {
authorizer := NewAuthorizer(nil)
allowPolicy := NewPolicy("test", "Test", Allow("test:resource:get"))
denyPolicy := NewPolicy("test", "Test", Deny("test:resource:delete"))
tests := []struct {
name string
action string
policies []*Policy
want bool
}{
{
name: "allowed",
action: "test:resource:get",
policies: []*Policy{allowPolicy},
want: true,
},
{
name: "denied",
action: "test:resource:delete",
policies: []*Policy{denyPolicy},
want: false,
},
{
name: "no match",
action: "test:resource:update",
policies: []*Policy{allowPolicy},
want: false,
},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := authorizer.IsAllowed(AuthorizeParams{
Action: tt.action,
Policies: tt.policies,
})
if got != tt.want {
t.Errorf("IsAllowed() = %v, want %v", got, tt.want)
}
})
}
}
func TestAccessDeniedError(t *testing.T) {
t.Run("error message without statement", func(t *testing.T) {
err := &AccessDeniedError{
Action: "test:resource:delete",
Reason: "no policy allows this action",
}
msg := err.Error()
if msg == "" {
t.Error("Expected non-empty error message")
}
})
t.Run("error message with statement SID", func(t *testing.T) {
err := &AccessDeniedError{
Action: "test:resource:delete",
Reason: "explicitly denied",
Statement: &Statement{
SID: "deny-delete",
},
}
msg := err.Error()
if msg == "" {
t.Error("Expected non-empty error message")
}
// Should contain the SID
if !contains(msg, "deny-delete") {
t.Errorf("Expected error message to contain SID, got %q", msg)
}
})
t.Run("unwrap returns ErrAccessDenied", func(t *testing.T) {
err := &AccessDeniedError{
Action: "test:resource:delete",
Reason: "no policy",
}
if !errors.Is(err, ErrAccessDenied) {
t.Error("Expected error to unwrap to ErrAccessDenied")
}
})
}
func contains(s, substr string) bool {
return len(s) >= len(substr) && (s == substr || len(s) > 0 && containsAt(s, substr, 0))
}
func containsAt(s, substr string, start int) bool {
for i := start; i <= len(s)-len(substr); i++ {
if s[i:i+len(substr)] == substr {
return true
}
}
return false
}

View File

@@ -20,60 +20,6 @@ import (
"go.probo.inc/probo/pkg/iam/policy"
)
func Example_definingActions() {
// Create an action registry
registry := policy.NewActionRegistry()
// Register actions for the IAM service
registry.MustRegister(policy.ActionDefinition{
Action: "iam:identity:get",
Service: "iam",
Resource: "identity",
Operation: "get",
Description: "Get identity details",
})
registry.MustRegister(policy.ActionDefinition{
Action: "iam:identity:update",
Service: "iam",
Resource: "identity",
Operation: "update",
Description: "Update identity",
})
// Register actions for the documents service
registry.MustRegister(policy.ActionDefinition{
Action: "documents:document:read",
Service: "documents",
Resource: "document",
Operation: "read",
Description: "Read a document",
})
registry.MustRegister(policy.ActionDefinition{
Action: "documents:document:write",
Service: "documents",
Resource: "document",
Operation: "write",
Description: "Create or update a document",
})
registry.MustRegister(policy.ActionDefinition{
Action: "documents:document:delete",
Service: "documents",
Resource: "document",
Operation: "delete",
Description: "Delete a document",
})
// List all actions for a service
docActions := registry.ByService("documents")
fmt.Printf("Documents service has %d actions\n", len(docActions))
// Output:
// Documents service has 3 actions
}
func Example_definingPolicies() {
// Define a viewer policy - can read everything
viewerPolicy := policy.NewPolicy("viewer", "Viewer Policy",

View File

@@ -107,27 +107,3 @@ func NotEquals(key string, values ...string) Condition {
}
}
// In creates an In condition.
func In(key string, values ...string) Condition {
return Condition{
Operator: ConditionIn,
Key: key,
Values: values,
}
}
// NotIn creates a NotIn condition.
func NotIn(key string, values ...string) Condition {
return Condition{
Operator: ConditionNotIn,
Key: key,
Values: values,
}
}
// ForEntityType creates a resource pattern for a specific entity type.
func ForEntityType(entityType uint16) ResourcePattern {
return ResourcePattern{
EntityType: &entityType,
}
}

View File

@@ -299,15 +299,12 @@ func TestConditionHelpers(t *testing.T) {
}
})
t.Run("In helper", func(t *testing.T) {
c := In("principal.role", "admin", "owner")
if c.Operator != ConditionIn {
t.Errorf("Expected ConditionIn, got %v", c.Operator)
t.Run("NotIn condition", func(t *testing.T) {
c := Condition{
Operator: ConditionNotIn,
Key: "principal.role",
Values: []string{"guest"},
}
})
t.Run("NotIn helper", func(t *testing.T) {
c := NotIn("principal.role", "guest")
if c.Operator != ConditionNotIn {
t.Errorf("Expected ConditionNotIn, got %v", c.Operator)
}

View File

@@ -31,25 +31,11 @@ type (
provider provider.Provider
scimClient *scimclient.Client
excludedUserNames []string
forceUpdate bool
dryRun bool
}
Option func(*Bridge)
)
func WithDryRun(dryRun bool) Option {
return func(s *Bridge) {
s.dryRun = dryRun
}
}
func WithForceUpdate(forceUpdate bool) Option {
return func(s *Bridge) {
s.forceUpdate = forceUpdate
}
}
func WithExcludedUserNames(excludedUserNames []string) Option {
return func(s *Bridge) {
s.excludedUserNames = excludedUserNames
@@ -96,16 +82,13 @@ func (s *Bridge) Run(ctx context.Context) (created, updated, deleted, deactivate
existingSCIM, exists := scimUsersByEmail[email]
if !exists {
if !s.dryRun {
if err := s.scimClient.CreateUser(ctx, &pu); err != nil {
errs = append(errs, fmt.Errorf("cannot create user %q: %w", pu.UserName, err))
continue
}
if err := s.scimClient.CreateUser(ctx, &pu); err != nil {
errs = append(errs, fmt.Errorf("cannot create user %q: %w", pu.UserName, err))
continue
}
created++
} else {
needsUpdate := s.forceUpdate ||
existingSCIM.Active != pu.Active ||
needsUpdate := existingSCIM.Active != pu.Active ||
existingSCIM.DisplayName != pu.DisplayName ||
existingSCIM.Title != pu.Title ||
existingSCIM.GivenName != pu.GivenName ||
@@ -120,11 +103,9 @@ func (s *Bridge) Run(ctx context.Context) (created, updated, deleted, deactivate
existingSCIM.PreferredLanguage != pu.PreferredLanguage
if needsUpdate {
if !s.dryRun {
if err := s.scimClient.UpdateUser(ctx, existingSCIM.ID, &pu); err != nil {
errs = append(errs, fmt.Errorf("cannot update user %q: %w", pu.UserName, err))
continue
}
if err := s.scimClient.UpdateUser(ctx, existingSCIM.ID, &pu); err != nil {
errs = append(errs, fmt.Errorf("cannot update user %q: %w", pu.UserName, err))
continue
}
updated++
} else {
@@ -139,11 +120,9 @@ func (s *Bridge) Run(ctx context.Context) (created, updated, deleted, deactivate
}
if s.isExcluded(email) {
if !s.dryRun {
if err := s.scimClient.DeleteUser(ctx, scimUser.ID); err != nil {
errs = append(errs, fmt.Errorf("cannot delete user %q: %w", email, err))
continue
}
if err := s.scimClient.DeleteUser(ctx, scimUser.ID); err != nil {
errs = append(errs, fmt.Errorf("cannot delete user %q: %w", email, err))
continue
}
deleted++
continue
@@ -153,11 +132,9 @@ func (s *Bridge) Run(ctx context.Context) (created, updated, deleted, deactivate
continue
}
if !s.dryRun {
if err := s.scimClient.DeactivateUser(ctx, scimUser.ID); err != nil {
errs = append(errs, fmt.Errorf("cannot deactivate user %q: %w", email, err))
continue
}
if err := s.scimClient.DeactivateUser(ctx, scimUser.ID); err != nil {
errs = append(errs, fmt.Errorf("cannot deactivate user %q: %w", email, err))
continue
}
deactivated++
}

View File

@@ -23,7 +23,6 @@ import (
"go.gearno.de/kit/pg"
"go.probo.inc/probo/pkg/coredata"
"go.probo.inc/probo/pkg/gid"
"go.probo.inc/probo/pkg/validator"
)
type (
@@ -36,18 +35,6 @@ func NewSessionService(svc *Service) *SessionService {
return &SessionService{Service: svc}
}
type (
RevokeAllSessionsRequest struct {
CurrentSessionID gid.GID
}
)
func (req RevokeAllSessionsRequest) Validate() error {
v := validator.New()
v.Check(req.CurrentSessionID, "current_session_id", validator.GID(coredata.SessionEntityType))
return v.Error()
}
func (s SessionService) GetSession(ctx context.Context, sessionID gid.GID) (*coredata.Session, error) {
var (
session = &coredata.Session{}