feat(epic2): Implement core authentication and authorization services
- Implement Audit Service (2.5) - gRPC server with Record and Query operations - Database persistence with audit schema - Service registry integration - Entry point: cmd/audit-service - Implement Identity Service (2.2) - User CRUD operations - Password hashing with argon2id - Email verification and password reset flows - Entry point: cmd/identity-service - Fix package naming conflicts in user_service.go - Implement Auth Service (2.1) - JWT token generation and validation - Login, RefreshToken, ValidateToken, Logout RPCs - Integration with Identity Service - Entry point: cmd/auth-service - Note: RefreshToken entity needs Ent generation - Implement Authz Service (2.3, 2.4) - Permission checking and authorization - User roles and permissions retrieval - RBAC-based authorization - Entry point: cmd/authz-service - Implement gRPC clients for all services - Auth, Identity, Authz, and Audit clients - Service discovery integration - Full gRPC communication - Add service configurations to config/default.yaml - Create SUMMARY.md with implementation details and testing instructions - Fix compilation errors in Identity Service (password package conflicts) - All services build successfully and tests pass
This commit is contained in:
@@ -5,39 +5,142 @@ import (
|
||||
"context"
|
||||
"fmt"
|
||||
|
||||
authzv1 "git.dcentral.systems/toolz/goplt/api/proto/generated/authz/v1"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/registry"
|
||||
"git.dcentral.systems/toolz/goplt/pkg/services"
|
||||
"google.golang.org/grpc"
|
||||
"google.golang.org/grpc/credentials/insecure"
|
||||
)
|
||||
|
||||
// AuthzClient implements AuthzServiceClient using gRPC.
|
||||
// This is a stub implementation - will be fully implemented when proto files are generated in Phase 4.
|
||||
type AuthzClient struct {
|
||||
registry registry.ServiceRegistry
|
||||
conn *grpc.ClientConn
|
||||
client authzv1.AuthzServiceClient
|
||||
}
|
||||
|
||||
// NewAuthzClient creates a new gRPC client for the Authz Service.
|
||||
func NewAuthzClient(reg registry.ServiceRegistry) (services.AuthzServiceClient, error) {
|
||||
return &AuthzClient{
|
||||
client := &AuthzClient{
|
||||
registry: reg,
|
||||
}, nil
|
||||
}
|
||||
return client, nil
|
||||
}
|
||||
|
||||
// connect connects to the Authz Service.
|
||||
func (c *AuthzClient) connect(ctx context.Context) error {
|
||||
if c.conn != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
instances, err := c.registry.Discover(ctx, "authz-service")
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to discover authz service: %w", err)
|
||||
}
|
||||
|
||||
if len(instances) == 0 {
|
||||
return fmt.Errorf("no instances found for authz-service")
|
||||
}
|
||||
|
||||
instance := instances[0]
|
||||
address := fmt.Sprintf("%s:%d", instance.Address, instance.Port)
|
||||
|
||||
conn, err := grpc.NewClient(address, grpc.WithTransportCredentials(insecure.NewCredentials()))
|
||||
if err != nil {
|
||||
return fmt.Errorf("failed to connect to authz-service at %s: %w", address, err)
|
||||
}
|
||||
|
||||
c.conn = conn
|
||||
c.client = authzv1.NewAuthzServiceClient(conn)
|
||||
return nil
|
||||
}
|
||||
|
||||
// Authorize checks if a user has a specific permission and returns an error if not.
|
||||
func (c *AuthzClient) Authorize(ctx context.Context, userID, permission string) error {
|
||||
return fmt.Errorf("not implemented: proto files not yet generated")
|
||||
if err := c.connect(ctx); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
resp, err := c.client.Authorize(ctx, &authzv1.AuthorizeRequest{
|
||||
UserId: userID,
|
||||
Permission: permission,
|
||||
})
|
||||
if err != nil {
|
||||
return fmt.Errorf("authorize failed: %w", err)
|
||||
}
|
||||
|
||||
if !resp.Authorized {
|
||||
return fmt.Errorf("unauthorized: %s", resp.Message)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasPermission checks if a user has a specific permission.
|
||||
func (c *AuthzClient) HasPermission(ctx context.Context, userID, permission string) (bool, error) {
|
||||
return false, fmt.Errorf("not implemented: proto files not yet generated")
|
||||
if err := c.connect(ctx); err != nil {
|
||||
return false, err
|
||||
}
|
||||
|
||||
resp, err := c.client.HasPermission(ctx, &authzv1.HasPermissionRequest{
|
||||
UserId: userID,
|
||||
Permission: permission,
|
||||
})
|
||||
if err != nil {
|
||||
return false, fmt.Errorf("has permission check failed: %w", err)
|
||||
}
|
||||
|
||||
return resp.HasPermission, nil
|
||||
}
|
||||
|
||||
// GetUserPermissions returns all permissions for a user.
|
||||
func (c *AuthzClient) GetUserPermissions(ctx context.Context, userID string) ([]services.Permission, error) {
|
||||
return nil, fmt.Errorf("not implemented: proto files not yet generated")
|
||||
if err := c.connect(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.client.GetUserPermissions(ctx, &authzv1.GetUserPermissionsRequest{
|
||||
UserId: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get user permissions failed: %w", err)
|
||||
}
|
||||
|
||||
permissions := make([]services.Permission, 0, len(resp.Permissions))
|
||||
for _, p := range resp.Permissions {
|
||||
permissions = append(permissions, services.Permission{
|
||||
ID: p.Id,
|
||||
Code: p.Code,
|
||||
Name: p.Name,
|
||||
Description: p.Description,
|
||||
})
|
||||
}
|
||||
|
||||
return permissions, nil
|
||||
}
|
||||
|
||||
// GetUserRoles returns all roles for a user.
|
||||
func (c *AuthzClient) GetUserRoles(ctx context.Context, userID string) ([]services.Role, error) {
|
||||
return nil, fmt.Errorf("not implemented: proto files not yet generated")
|
||||
if err := c.connect(ctx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
resp, err := c.client.GetUserRoles(ctx, &authzv1.GetUserRolesRequest{
|
||||
UserId: userID,
|
||||
})
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("get user roles failed: %w", err)
|
||||
}
|
||||
|
||||
roles := make([]services.Role, 0, len(resp.Roles))
|
||||
for _, r := range resp.Roles {
|
||||
roles = append(roles, services.Role{
|
||||
ID: r.Id,
|
||||
Name: r.Name,
|
||||
Description: r.Description,
|
||||
Permissions: r.Permissions,
|
||||
})
|
||||
}
|
||||
|
||||
return roles, nil
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user