Files
magistrala/pkg/authz/authsvc/authz.go
T
2025-07-15 11:24:41 +02:00

144 lines
4.1 KiB
Go

// Copyright (c) Abstract Machines
// SPDX-License-Identifier: Apache-2.0
package authsvc
import (
"context"
grpcAuthV1 "github.com/absmach/supermq/api/grpc/auth/v1"
"github.com/absmach/supermq/auth/api/grpc/auth"
"github.com/absmach/supermq/domains"
"github.com/absmach/supermq/pkg/authz"
pkgDomians "github.com/absmach/supermq/pkg/domains"
"github.com/absmach/supermq/pkg/errors"
svcerr "github.com/absmach/supermq/pkg/errors/service"
"github.com/absmach/supermq/pkg/grpcclient"
"github.com/absmach/supermq/pkg/policies"
grpchealth "google.golang.org/grpc/health/grpc_health_v1"
)
type authorization struct {
authSvcClient grpcAuthV1.AuthServiceClient
domains pkgDomians.Authorization
}
var _ authz.Authorization = (*authorization)(nil)
func NewAuthorization(ctx context.Context, cfg grpcclient.Config, domainsAuthz pkgDomians.Authorization) (authz.Authorization, grpcclient.Handler, error) {
client, err := grpcclient.NewHandler(cfg)
if err != nil {
return nil, nil, err
}
health := grpchealth.NewHealthClient(client.Connection())
resp, err := health.Check(ctx, &grpchealth.HealthCheckRequest{
Service: "auth",
})
if err != nil || resp.GetStatus() != grpchealth.HealthCheckResponse_SERVING {
return nil, nil, grpcclient.ErrSvcNotServing
}
authSvcClient := auth.NewAuthClient(client.Connection(), cfg.Timeout)
return authorization{
authSvcClient: authSvcClient,
domains: domainsAuthz,
}, client, nil
}
func (a authorization) Authorize(ctx context.Context, pr authz.PolicyReq) error {
if pr.SubjectType == policies.UserType && (pr.ObjectType == policies.GroupType || pr.ObjectType == policies.ClientType || pr.ObjectType == policies.DomainType) {
domainID := pr.Domain
if domainID == "" {
if pr.ObjectType != policies.DomainType {
return svcerr.ErrDomainAuthorization
}
domainID = pr.Object
}
if err := a.checkDomain(ctx, pr.SubjectType, pr.Subject, domainID); err != nil {
return errors.Wrap(svcerr.ErrDomainAuthorization, err)
}
}
req := grpcAuthV1.AuthZReq{
Domain: pr.Domain,
SubjectType: pr.SubjectType,
SubjectKind: pr.SubjectKind,
SubjectRelation: pr.SubjectRelation,
Subject: pr.Subject,
Relation: pr.Relation,
Permission: pr.Permission,
Object: pr.Object,
ObjectType: pr.ObjectType,
}
res, err := a.authSvcClient.Authorize(ctx, &req)
if err != nil {
return errors.Wrap(errors.ErrAuthorization, err)
}
if !res.GetAuthorized() {
return errors.ErrAuthorization
}
return nil
}
func (a authorization) checkDomain(ctx context.Context, subjectType, subject, domainID string) error {
status, err := a.domains.RetrieveStatus(ctx, domainID)
if err != nil {
return errors.Wrap(svcerr.ErrViewEntity, err)
}
switch status {
case domains.FreezeStatus:
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
Subject: subject,
SubjectType: subjectType,
Permission: policies.AdminPermission,
Object: policies.SuperMQObject,
ObjectType: policies.PlatformType,
})
return err
case domains.DisabledStatus:
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
Subject: subject,
SubjectType: subjectType,
Permission: policies.AdminPermission,
Object: domainID,
ObjectType: policies.DomainType,
})
return err
case domains.EnabledStatus:
_, err := a.authSvcClient.Authorize(ctx, &grpcAuthV1.AuthZReq{
Subject: subject,
SubjectType: subjectType,
Permission: policies.MembershipPermission,
Object: domainID,
ObjectType: policies.DomainType,
})
return err
default:
return svcerr.ErrInvalidStatus
}
}
func (a authorization) AuthorizePAT(ctx context.Context, pr authz.PatReq) error {
req := grpcAuthV1.AuthZPatReq{
UserId: pr.UserID,
PatId: pr.PatID,
EntityType: uint32(pr.EntityType),
OptionalDomainId: pr.OptionalDomainID,
Operation: uint32(pr.Operation),
EntityId: pr.EntityID,
}
res, err := a.authSvcClient.AuthorizePAT(ctx, &req)
if err != nil {
return errors.Wrap(errors.ErrAuthorization, err)
}
if !res.Authorized {
return errors.ErrAuthorization
}
return nil
}