Files
goplt/internal/client/grpc/authz_client.go
0x1d b1b895e818 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
2025-11-06 20:07:20 +01:00

147 lines
3.8 KiB
Go

// Package grpc provides gRPC client implementations for service clients.
package grpc
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.
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) {
client := &AuthzClient{
registry: reg,
}
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 {
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) {
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) {
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) {
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
}