Zanzana: Optimize batch check (#96669)
* Restructure check code so we only check namespace access once for each GroupResource during for batch
This commit is contained in:
@@ -14,28 +14,39 @@ func (s *Server) Check(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.C
|
||||
ctx, span := tracer.Start(ctx, "authzServer.Check")
|
||||
defer span.End()
|
||||
|
||||
if info, ok := common.GetTypeInfo(r.GetGroup(), r.GetResource()); ok {
|
||||
return s.checkTyped(ctx, r, info)
|
||||
}
|
||||
return s.checkGeneric(ctx, r)
|
||||
}
|
||||
|
||||
// checkNamespace checks if subject has access through namespace
|
||||
func (s *Server) checkNamespace(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.CheckResponse, error) {
|
||||
storeInf, err := s.getStoreInfo(ctx, r.Namespace)
|
||||
store, err := s.getStoreInfo(ctx, r.GetNamespace())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
relation := common.VerbMapping[r.GetVerb()]
|
||||
|
||||
// Check if subject has access through namespace
|
||||
res, err := s.checkNamespace(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), store)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if res.GetAllowed() {
|
||||
return res, nil
|
||||
}
|
||||
|
||||
if info, ok := common.GetTypeInfo(r.GetGroup(), r.GetResource()); ok {
|
||||
return s.checkTyped(ctx, r.GetSubject(), relation, r.GetName(), info, store)
|
||||
}
|
||||
return s.checkGeneric(ctx, r.GetSubject(), relation, r.GetGroup(), r.GetResource(), r.GetName(), r.GetFolder(), store)
|
||||
}
|
||||
|
||||
// checkTyped performes check on the root "namespace". If subject has access through the namespace they have access to
|
||||
// every resource for that "GroupResource".
|
||||
func (s *Server) checkNamespace(ctx context.Context, subject, relation, group, resource string, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
||||
StoreId: storeInf.Id,
|
||||
AuthorizationModelId: storeInf.AuthorizationModelId,
|
||||
StoreId: store.ID,
|
||||
AuthorizationModelId: store.ModelID,
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
User: r.GetSubject(),
|
||||
User: subject,
|
||||
Relation: relation,
|
||||
Object: common.NewNamespaceResourceIdent(r.GetGroup(), r.GetResource()),
|
||||
Object: common.NewNamespaceResourceIdent(group, resource),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -45,22 +56,16 @@ func (s *Server) checkNamespace(ctx context.Context, r *authzv1.CheckRequest) (*
|
||||
return &authzv1.CheckResponse{Allowed: res.GetAllowed()}, nil
|
||||
}
|
||||
|
||||
func (s *Server) checkTyped(ctx context.Context, r *authzv1.CheckRequest, info common.TypeInfo) (*authzv1.CheckResponse, error) {
|
||||
storeInf, err := s.getStoreInfo(ctx, r.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
relation := common.VerbMapping[r.GetVerb()]
|
||||
|
||||
// 1. check if subject has direct access to resource
|
||||
// checkTyped performes checks on our typed resources e.g. folder.
|
||||
func (s *Server) checkTyped(ctx context.Context, subject, relation, name string, info common.TypeInfo, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||
// Check if subject has direct access to resource
|
||||
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
||||
StoreId: storeInf.Id,
|
||||
AuthorizationModelId: storeInf.AuthorizationModelId,
|
||||
StoreId: store.ID,
|
||||
AuthorizationModelId: store.ModelID,
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
User: r.GetSubject(),
|
||||
User: subject,
|
||||
Relation: relation,
|
||||
Object: common.NewTypedIdent(info.Type, r.GetName()),
|
||||
Object: common.NewTypedIdent(info.Type, name),
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
@@ -71,40 +76,30 @@ func (s *Server) checkTyped(ctx context.Context, r *authzv1.CheckRequest, info c
|
||||
return &authzv1.CheckResponse{Allowed: true}, nil
|
||||
}
|
||||
|
||||
// 2. check if subject has access through namespace
|
||||
nsRes, err := s.checkNamespace(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return &authzv1.CheckResponse{Allowed: nsRes.GetAllowed()}, nil
|
||||
return &authzv1.CheckResponse{Allowed: false}, nil
|
||||
}
|
||||
|
||||
func (s *Server) checkGeneric(ctx context.Context, r *authzv1.CheckRequest) (*authzv1.CheckResponse, error) {
|
||||
storeInf, err := s.getStoreInfo(ctx, r.Namespace)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// checkGeneric check our generic "resource" type.
|
||||
func (s *Server) checkGeneric(ctx context.Context, subject, relation, group, resource, name, folder string, store *storeInfo) (*authzv1.CheckResponse, error) {
|
||||
groupResource := structpb.NewStringValue(common.FormatGroupResource(group, resource))
|
||||
|
||||
relation := common.VerbMapping[r.GetVerb()]
|
||||
// 1. check if subject has direct access to resource
|
||||
// Check if subject has direct access to resource
|
||||
res, err := s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
||||
StoreId: storeInf.Id,
|
||||
AuthorizationModelId: storeInf.AuthorizationModelId,
|
||||
StoreId: store.ID,
|
||||
AuthorizationModelId: store.ModelID,
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
User: r.GetSubject(),
|
||||
User: subject,
|
||||
Relation: relation,
|
||||
Object: common.NewResourceIdent(r.GetGroup(), r.GetResource(), r.GetName()),
|
||||
Object: common.NewResourceIdent(group, resource, name),
|
||||
},
|
||||
Context: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"requested_group": structpb.NewStringValue(common.FormatGroupResource(r.GetGroup(), r.GetResource())),
|
||||
"requested_group": groupResource,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
if err != nil {
|
||||
// FIXME: wrap error
|
||||
return nil, err
|
||||
}
|
||||
|
||||
@@ -112,32 +107,22 @@ func (s *Server) checkGeneric(ctx context.Context, r *authzv1.CheckRequest) (*au
|
||||
return &authzv1.CheckResponse{Allowed: true}, nil
|
||||
}
|
||||
|
||||
// 2. check if subject has access through namespace
|
||||
nsRes, err := s.checkNamespace(ctx, r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if nsRes.GetAllowed() {
|
||||
return &authzv1.CheckResponse{Allowed: true}, nil
|
||||
}
|
||||
|
||||
if r.Folder == "" {
|
||||
if folder == "" {
|
||||
return &authzv1.CheckResponse{Allowed: false}, nil
|
||||
}
|
||||
|
||||
// 3. check if subject has access as a sub resource for the folder
|
||||
// Check if subject has access as a sub resource for the folder
|
||||
res, err = s.openfga.Check(ctx, &openfgav1.CheckRequest{
|
||||
StoreId: storeInf.Id,
|
||||
AuthorizationModelId: storeInf.AuthorizationModelId,
|
||||
StoreId: store.ID,
|
||||
AuthorizationModelId: store.ModelID,
|
||||
TupleKey: &openfgav1.CheckRequestTupleKey{
|
||||
User: r.GetSubject(),
|
||||
User: subject,
|
||||
Relation: common.FolderResourceRelation(relation),
|
||||
Object: common.NewFolderIdent(r.GetFolder()),
|
||||
Object: common.NewFolderIdent(folder),
|
||||
},
|
||||
Context: &structpb.Struct{
|
||||
Fields: map[string]*structpb.Value{
|
||||
"requested_group": structpb.NewStringValue(common.FormatGroupResource(r.GetGroup(), r.GetResource())),
|
||||
"requested_group": groupResource,
|
||||
},
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user