@@ -3,6 +3,12 @@ package sar
33import (
44 "context"
55 "fmt"
6+ "sync"
7+
8+ "google.golang.org/grpc"
9+ "k8s.io/klog/v2"
10+ pbv1 "open-cluster-management.io/sdk-go/pkg/cloudevents/generic/options/grpc/protobuf/v1"
11+ "open-cluster-management.io/sdk-go/pkg/server/grpc/authz"
612
713 authv1 "k8s.io/api/authorization/v1"
814 metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
@@ -16,41 +22,124 @@ import (
1622 "open-cluster-management.io/sdk-go/pkg/cloudevents/clients/lease"
1723 "open-cluster-management.io/sdk-go/pkg/cloudevents/clients/work/payload"
1824 "open-cluster-management.io/sdk-go/pkg/cloudevents/generic/types"
19- "open-cluster-management.io/sdk-go/pkg/cloudevents/server/grpc/authn"
20- "open-cluster-management.io/sdk-go/pkg/cloudevents/server/grpc/authz"
25+ "open-cluster-management.io/sdk-go/pkg/server/grpc/authn"
2126)
2227
2328type SARAuthorizer struct {
2429 kubeClient kubernetes.Interface
2530}
2631
27- func NewSARAuthorizer (kubeClient kubernetes.Interface ) authz.Authorizer {
32+ // validate SARAuthorizer implement StreamAuthorizer and UnaryAuthorizer
33+ var _ authz.StreamAuthorizer = (* SARAuthorizer )(nil )
34+ var _ authz.UnaryAuthorizer = (* SARAuthorizer )(nil )
35+
36+ // wrappedAuthorizedStream caches the subscription request that is already read.
37+ type wrappedAuthorizedStream struct {
38+ sync.Mutex
39+
40+ grpc.ServerStream
41+ authorizedReq * pbv1.SubscriptionRequest
42+ }
43+
44+ // RecvMsg set the msg from the cache.
45+ func (c * wrappedAuthorizedStream ) RecvMsg (m any ) error {
46+ c .Lock ()
47+ defer c .Unlock ()
48+
49+ msg , ok := m .(* pbv1.SubscriptionRequest )
50+ if ! ok {
51+ return fmt .Errorf ("unsupported request type %T" , m )
52+ }
53+
54+ msg .ClusterName = c .authorizedReq .ClusterName
55+ msg .Source = c .authorizedReq .Source
56+ msg .DataType = c .authorizedReq .DataType
57+ return nil
58+ }
59+
60+ func NewSARAuthorizer (kubeClient kubernetes.Interface ) * SARAuthorizer {
2861 return & SARAuthorizer {
2962 kubeClient : kubeClient ,
3063 }
3164}
3265
33- func (s * SARAuthorizer ) Authorize (ctx context.Context , cluster string , eventsType types.CloudEventsType ) error {
66+ func (s * SARAuthorizer ) AuthorizeRequest (ctx context.Context , req any ) (authz.Decision , error ) {
67+ pReq , ok := req .(* pbv1.PublishRequest )
68+ if ! ok {
69+ return authz .DecisionDeny , fmt .Errorf ("unsupported request type %T" , req )
70+ }
71+
72+ eventsType , err := types .ParseCloudEventsType (pReq .Event .Type )
73+ if err != nil {
74+ return authz .DecisionDeny , err
75+ }
76+
77+ // the event of grpc publish request is the original cloudevent data, we need a `ce-` prefix
78+ // to get the event attribute
79+ clusterAttr , ok := pReq .Event .Attributes [fmt .Sprintf ("ce-%s" , types .ExtensionClusterName )]
80+ if ! ok {
81+ return authz .DecisionDeny , fmt .Errorf ("missing ce-clustername in event attributes, %v" , pReq .Event .Attributes )
82+ }
83+
84+ decision , err := s .authorize (ctx , clusterAttr .GetCeString (), * eventsType )
85+ return decision , err
86+ }
87+
88+ func (s * SARAuthorizer ) AuthorizeStream (ctx context.Context , ss grpc.ServerStream , info * grpc.StreamServerInfo ) (authz.Decision , grpc.ServerStream , error ) {
89+ if info .FullMethod != pbv1 .CloudEventService_Subscribe_FullMethodName {
90+ klog .V (4 ).Infof ("unsupported service full method %s for SARAuthorizer" , info .FullMethod )
91+ return authz .DecisionNoOpinion , nil , nil
92+ }
93+
94+ if info .IsClientStream {
95+ return authz .DecisionAllow , ss , nil
96+ }
97+
98+ var req pbv1.SubscriptionRequest
99+ if err := ss .RecvMsg (& req ); err != nil {
100+ return authz .DecisionDeny , nil , err
101+ }
102+
103+ eventDataType , err := types .ParseCloudEventsDataType (req .DataType )
104+ if err != nil {
105+ return authz .DecisionDeny , nil , err
106+ }
107+
108+ eventsType := types.CloudEventsType {
109+ CloudEventsDataType : * eventDataType ,
110+ SubResource : types .SubResourceSpec ,
111+ Action : types .WatchRequestAction ,
112+ }
113+
114+ decision , err := s .authorize (ss .Context (), req .ClusterName , eventsType )
115+ if err != nil {
116+ return decision , nil , err
117+ }
118+
119+ return decision , & wrappedAuthorizedStream {ServerStream : ss , authorizedReq : & req }, nil
120+ }
121+
122+ func (s * SARAuthorizer ) authorize (ctx context.Context , cluster string , eventsType types.CloudEventsType ) (authz.Decision , error ) {
34123 user , groups , err := userInfo (ctx )
35124 if err != nil {
36- return err
125+ return authz . DecisionDeny , err
37126 }
38127
39128 sar , err := toSubjectAccessReview (cluster , user , groups , eventsType )
40129 if err != nil {
41- return err
130+ return authz . DecisionDeny , err
42131 }
43132
44133 created , err := s .kubeClient .AuthorizationV1 ().SubjectAccessReviews ().Create (
45134 ctx , sar , metav1.CreateOptions {})
46135 if err != nil {
47- return err
136+ return authz . DecisionDeny , err
48137 }
49138 if ! created .Status .Allowed {
50- return fmt .Errorf ("the event %s is not allowed, (cluster=%s, sar=%v, reason=%v)" ,
139+ return authz . DecisionDeny , fmt .Errorf ("the event %s is not allowed, (cluster=%s, sar=%v, reason=%v)" ,
51140 eventsType , cluster , sar .Spec , created .Status )
52141 }
53- return nil
142+ return authz . DecisionAllow , nil
54143}
55144
56145func userInfo (ctx context.Context ) (user string , groups []string , err error ) {
0 commit comments