feat: add commands for managing organizations and users

Introduce `AddUserToOrganization`, `RemoveAPIKey`, and 
`RemoveOrganization` commands to enhance organization 
management. Implement validation for user addition and 
API key removal. Update GraphQL schema to support new 
mutations and add caching for the new events, ensuring 
that organizations and their relationships are accurately 
represented in the cache.
This commit is contained in:
2025-11-22 18:37:07 +01:00
parent 335a9f3b54
commit ffcf41b85a
14 changed files with 1500 additions and 30 deletions
+62 -4
View File
@@ -67,6 +67,37 @@ func UserFromContext(ctx context.Context) string {
return ""
}
func UserHasRole(ctx context.Context, role string) bool {
token, err := TokenFromContext(ctx)
if err != nil || token == nil {
return false
}
claims, ok := token.Claims.(jwt.MapClaims)
if !ok {
return false
}
// Check the custom roles claim
rolesInterface, ok := claims["https://unbound.se/roles"]
if !ok {
return false
}
roles, ok := rolesInterface.([]interface{})
if !ok {
return false
}
for _, r := range roles {
if roleStr, ok := r.(string); ok && roleStr == role {
return true
}
}
return false
}
func OrganizationFromContext(ctx context.Context) string {
if value := ctx.Value(OrganizationKey); value != nil {
if u, ok := value.(domain.Organization); ok {
@@ -77,15 +108,42 @@ func OrganizationFromContext(ctx context.Context) string {
}
func (m *AuthMiddleware) Directive(ctx context.Context, _ interface{}, next graphql.Resolver, user *bool, organization *bool) (res interface{}, err error) {
if user != nil && *user {
if u := UserFromContext(ctx); u == "" {
userRequired := user != nil && *user
orgRequired := organization != nil && *organization
u := UserFromContext(ctx)
orgId := OrganizationFromContext(ctx)
fmt.Printf("[Auth Directive] userRequired=%v, orgRequired=%v, hasUser=%v, hasOrg=%v\n",
userRequired, orgRequired, u != "", orgId != "")
// If both are required, it means EITHER is acceptable (OR logic)
if userRequired && orgRequired {
if u == "" && orgId == "" {
fmt.Printf("[Auth Directive] REJECTED: Neither user nor organization available\n")
return nil, fmt.Errorf("authentication required: provide either user token or organization API key")
}
fmt.Printf("[Auth Directive] ACCEPTED: Has user=%v OR organization=%v\n", u != "", orgId != "")
return next(ctx)
}
// Only user required
if userRequired {
if u == "" {
fmt.Printf("[Auth Directive] REJECTED: No user available\n")
return nil, fmt.Errorf("no user available in request")
}
fmt.Printf("[Auth Directive] ACCEPTED: User authenticated\n")
}
if organization != nil && *organization {
if orgId := OrganizationFromContext(ctx); orgId == "" {
// Only organization required
if orgRequired {
if orgId == "" {
fmt.Printf("[Auth Directive] REJECTED: No organization available\n")
return nil, fmt.Errorf("no organization available in request")
}
fmt.Printf("[Auth Directive] ACCEPTED: Organization authenticated\n")
}
return next(ctx)
}