forked from openshift/origin
/
personal_subjectaccessreview.go
130 lines (106 loc) · 4.07 KB
/
personal_subjectaccessreview.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
package authorizer
import (
"bytes"
"io/ioutil"
"net/http"
"github.com/openshift/origin/pkg/api/legacy"
authorizationapi "github.com/openshift/origin/pkg/authorization/apis/authorization"
authorizationv1helpers "github.com/openshift/origin/pkg/authorization/apis/authorization/v1"
"k8s.io/apimachinery/pkg/runtime"
"k8s.io/apimachinery/pkg/runtime/schema"
"k8s.io/apimachinery/pkg/runtime/serializer"
utilruntime "k8s.io/apimachinery/pkg/util/runtime"
"k8s.io/apiserver/pkg/endpoints/request"
apirequest "k8s.io/apiserver/pkg/endpoints/request"
)
type personalSARRequestInfoResolver struct {
// infoFactory is used to determine info for the request
infoFactory apirequest.RequestInfoResolver
}
func NewPersonalSARRequestInfoResolver(infoFactory apirequest.RequestInfoResolver) apirequest.RequestInfoResolver {
return &personalSARRequestInfoResolver{
infoFactory: infoFactory,
}
}
func (a *personalSARRequestInfoResolver) NewRequestInfo(req *http.Request) (*request.RequestInfo, error) {
requestInfo, err := a.infoFactory.NewRequestInfo(req)
if err != nil {
return requestInfo, err
}
// only match SAR and LSAR requests for personal review
switch {
case !requestInfo.IsResourceRequest:
return requestInfo, nil
case len(requestInfo.APIGroup) != 0 && requestInfo.APIGroup != "authorization.openshift.io":
return requestInfo, nil
case len(requestInfo.Subresource) != 0:
return requestInfo, nil
case requestInfo.Verb != "create":
return requestInfo, nil
case requestInfo.Resource != "subjectaccessreviews" && requestInfo.Resource != "localsubjectaccessreviews":
return requestInfo, nil
}
// at this point we're probably running a SAR or LSAR. Decode the body and check. This is expensive.
isSelfSAR, err := isPersonalAccessReviewFromRequest(req, requestInfo)
if err != nil {
return nil, err
}
if !isSelfSAR {
return requestInfo, nil
}
// if we do have a self-SAR, rewrite the requestInfo to indicate this is a selfsubjectaccessreviews.authorization.k8s.io request
requestInfo.APIGroup = "authorization.k8s.io"
requestInfo.Resource = "selfsubjectaccessreviews"
return requestInfo, nil
}
// isPersonalAccessReviewFromRequest this variant handles the case where we have an httpRequest
func isPersonalAccessReviewFromRequest(req *http.Request, requestInfo *request.RequestInfo) (bool, error) {
// TODO once we're integrated with the api installer, we should have direct access to the deserialized content
// for now, this only happens on subjectaccessreviews with a personal check, pay the double retrieve and decode cost
body, err := ioutil.ReadAll(req.Body)
if err != nil {
return false, err
}
req.Body = ioutil.NopCloser(bytes.NewBuffer(body))
defaultGVK := schema.GroupVersionKind{Version: requestInfo.APIVersion, Group: requestInfo.APIGroup}
switch requestInfo.Resource {
case "subjectaccessreviews":
defaultGVK.Kind = "SubjectAccessReview"
case "localsubjectaccessreviews":
defaultGVK.Kind = "LocalSubjectAccessReview"
}
obj, _, err := sarCodecFactory.UniversalDecoder().Decode(body, &defaultGVK, nil)
if err != nil {
return false, err
}
switch castObj := obj.(type) {
case *authorizationapi.SubjectAccessReview:
return IsPersonalAccessReviewFromSAR(castObj), nil
case *authorizationapi.LocalSubjectAccessReview:
return isPersonalAccessReviewFromLocalSAR(castObj), nil
default:
return false, nil
}
}
// IsPersonalAccessReviewFromSAR this variant handles the case where we have an SAR
func IsPersonalAccessReviewFromSAR(sar *authorizationapi.SubjectAccessReview) bool {
if len(sar.User) == 0 && len(sar.Groups) == 0 {
return true
}
return false
}
// isPersonalAccessReviewFromLocalSAR this variant handles the case where we have a local SAR
func isPersonalAccessReviewFromLocalSAR(sar *authorizationapi.LocalSubjectAccessReview) bool {
if len(sar.User) == 0 && len(sar.Groups) == 0 {
return true
}
return false
}
var (
sarScheme = runtime.NewScheme()
sarCodecFactory = serializer.NewCodecFactory(sarScheme)
)
func init() {
legacy.InstallInternalLegacyAuthorization(sarScheme)
utilruntime.Must(authorizationv1helpers.Install(sarScheme))
}