This commit is contained in:
2025-08-22 19:53:42 -04:00
parent c85812ddb6
commit 738499037f
4 changed files with 191 additions and 14 deletions

View File

@ -269,14 +269,81 @@ func (r *GrantedPermissionRepository) GrantPermissions(ctx context.Context, gran
// GetGrantedPermissions retrieves all granted permissions for a token // GetGrantedPermissions retrieves all granted permissions for a token
func (r *GrantedPermissionRepository) GetGrantedPermissions(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID) ([]*domain.GrantedPermission, error) { func (r *GrantedPermissionRepository) GetGrantedPermissions(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID) ([]*domain.GrantedPermission, error) {
// TODO: Implement actual granted permissions retrieval query := `
return []*domain.GrantedPermission{}, nil SELECT id, token_type, token_id, permission_id, scope, created_at, created_by, revoked
FROM granted_permissions
WHERE token_type = $1 AND token_id = $2 AND revoked = false
ORDER BY created_at ASC
`
db := r.db.GetDB().(*sql.DB)
rows, err := db.QueryContext(ctx, query, string(tokenType), tokenID)
if err != nil {
return nil, fmt.Errorf("failed to query granted permissions: %w", err)
}
defer rows.Close()
var permissions []*domain.GrantedPermission
for rows.Next() {
perm := &domain.GrantedPermission{}
var tokenTypeStr string
err := rows.Scan(
&perm.ID,
&tokenTypeStr,
&perm.TokenID,
&perm.PermissionID,
&perm.Scope,
&perm.CreatedAt,
&perm.CreatedBy,
&perm.Revoked,
)
if err != nil {
return nil, fmt.Errorf("failed to scan granted permission: %w", err)
}
perm.TokenType = domain.TokenType(tokenTypeStr)
permissions = append(permissions, perm)
}
if err = rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating granted permissions: %w", err)
}
return permissions, nil
} }
// GetGrantedPermissionScopes retrieves only the scopes for a token (more efficient) // GetGrantedPermissionScopes retrieves only the scopes for a token (more efficient)
func (r *GrantedPermissionRepository) GetGrantedPermissionScopes(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID) ([]string, error) { func (r *GrantedPermissionRepository) GetGrantedPermissionScopes(ctx context.Context, tokenType domain.TokenType, tokenID uuid.UUID) ([]string, error) {
// TODO: Implement actual scope retrieval query := `
return []string{}, nil SELECT scope
FROM granted_permissions
WHERE token_type = $1 AND token_id = $2 AND revoked = false
ORDER BY scope ASC
`
db := r.db.GetDB().(*sql.DB)
rows, err := db.QueryContext(ctx, query, string(tokenType), tokenID)
if err != nil {
return nil, fmt.Errorf("failed to query granted permission scopes: %w", err)
}
defer rows.Close()
var scopes []string
for rows.Next() {
var scope string
if err := rows.Scan(&scope); err != nil {
return nil, fmt.Errorf("failed to scan permission scope: %w", err)
}
scopes = append(scopes, scope)
}
if err = rows.Err(); err != nil {
return nil, fmt.Errorf("error iterating permission scopes: %w", err)
}
return scopes, nil
} }
// RevokePermission revokes a specific permission from a token // RevokePermission revokes a specific permission from a token

View File

@ -208,15 +208,122 @@ func (s *tokenService) GenerateUserToken(ctx context.Context, appID, userID stri
func (s *tokenService) VerifyToken(ctx context.Context, req *domain.VerifyRequest) (*domain.VerifyResponse, error) { func (s *tokenService) VerifyToken(ctx context.Context, req *domain.VerifyRequest) (*domain.VerifyResponse, error) {
s.logger.Debug("Verifying token", zap.String("app_id", req.AppID), zap.String("type", string(req.Type))) s.logger.Debug("Verifying token", zap.String("app_id", req.AppID), zap.String("type", string(req.Type)))
// TODO: Implement actual token verification logic // Validate request
response := &domain.VerifyResponse{ if req.Token == "" {
Valid: true, return &domain.VerifyResponse{
UserID: req.UserID, Valid: false,
Permissions: []string{"basic"}, Error: "Token is required",
TokenType: req.Type, }, nil
} }
return response, nil // Validate application exists
app, err := s.appRepo.GetByID(ctx, req.AppID)
if err != nil {
s.logger.Error("Failed to get application", zap.Error(err), zap.String("app_id", req.AppID))
return &domain.VerifyResponse{
Valid: false,
Error: "Invalid application",
}, nil
}
switch req.Type {
case domain.TokenTypeStatic:
return s.verifyStaticToken(ctx, req, app)
case domain.TokenTypeUser:
return s.verifyUserToken(ctx, req, app)
default:
return &domain.VerifyResponse{
Valid: false,
Error: "Invalid token type",
}, nil
}
}
// verifyStaticToken verifies a static token
func (s *tokenService) verifyStaticToken(ctx context.Context, req *domain.VerifyRequest, app *domain.Application) (*domain.VerifyResponse, error) {
s.logger.Debug("Verifying static token", zap.String("app_id", req.AppID))
// Check token format
if !crypto.IsValidTokenFormat(req.Token) {
s.logger.Warn("Invalid token format", zap.String("app_id", req.AppID))
return &domain.VerifyResponse{
Valid: false,
Error: "Invalid token format",
}, nil
}
// Try to find token by testing against all stored hashes for this app
tokens, err := s.tokenRepo.GetByAppID(ctx, req.AppID)
if err != nil {
s.logger.Error("Failed to get tokens for app", zap.Error(err), zap.String("app_id", req.AppID))
return &domain.VerifyResponse{
Valid: false,
Error: "Token verification failed",
}, nil
}
var matchedToken *domain.StaticToken
for _, token := range tokens {
if s.tokenGen.VerifyToken(req.Token, token.KeyHash) {
matchedToken = token
break
}
}
if matchedToken == nil {
s.logger.Warn("Token not found or invalid", zap.String("app_id", req.AppID))
return &domain.VerifyResponse{
Valid: false,
Error: "Invalid token",
}, nil
}
// Get granted permissions for this token
permissions, err := s.grantRepo.GetGrantedPermissionScopes(ctx, domain.TokenTypeStatic, matchedToken.ID)
if err != nil {
s.logger.Error("Failed to get token permissions", zap.Error(err), zap.String("token_id", matchedToken.ID.String()))
return &domain.VerifyResponse{
Valid: false,
Error: "Failed to retrieve permissions",
}, nil
}
// Check specific permissions if requested
var permissionResults map[string]bool
if len(req.Permissions) > 0 {
permissionResults, err = s.grantRepo.HasAnyPermission(ctx, domain.TokenTypeStatic, matchedToken.ID, req.Permissions)
if err != nil {
s.logger.Error("Failed to check specific permissions", zap.Error(err))
return &domain.VerifyResponse{
Valid: false,
Error: "Failed to check permissions",
}, nil
}
}
s.logger.Info("Static token verified successfully",
zap.String("token_id", matchedToken.ID.String()),
zap.String("app_id", req.AppID),
zap.Strings("permissions", permissions))
return &domain.VerifyResponse{
Valid: true,
Permissions: permissions,
PermissionResults: permissionResults,
TokenType: domain.TokenTypeStatic,
}, nil
}
// verifyUserToken verifies a user token (JWT-based)
func (s *tokenService) verifyUserToken(ctx context.Context, req *domain.VerifyRequest, app *domain.Application) (*domain.VerifyResponse, error) {
s.logger.Debug("Verifying user token", zap.String("app_id", req.AppID))
// TODO: Implement JWT token verification
// For now, return an error since user tokens are not fully implemented
return &domain.VerifyResponse{
Valid: false,
Error: "User token verification not yet implemented",
}, nil
} }
// RenewUserToken renews a user token // RenewUserToken renews a user token

BIN
server

Binary file not shown.

View File

@ -420,12 +420,15 @@ func (suite *IntegrationTestSuite) TestStaticTokenWorkflow() {
assert.True(t, verifyResp.Valid) assert.True(t, verifyResp.Valid)
assert.Equal(t, domain.TokenTypeStatic, verifyResp.TokenType) assert.Equal(t, domain.TokenTypeStatic, verifyResp.TokenType)
// Note: The current service implementation returns ["basic"] as a placeholder // Verify that we get the actual permissions that were granted to the token
assert.Contains(t, verifyResp.Permissions, "basic") assert.Contains(t, verifyResp.Permissions, "repo.read")
assert.Contains(t, verifyResp.Permissions, "repo.write")
if verifyResp.PermissionResults != nil { if verifyResp.PermissionResults != nil {
// Check that we get some permission results // Check that we get permission results for the requested permissions
assert.NotEmpty(t, verifyResp.PermissionResults) assert.NotEmpty(t, verifyResp.PermissionResults)
// The token should have the "repo.read" permission we requested
assert.True(t, verifyResp.PermissionResults["repo.read"])
} }
}) })
} }