forked from openshift/origin
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathrest.go
134 lines (115 loc) · 4.96 KB
/
rest.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
131
132
133
134
package subjectaccessreview
import (
"errors"
"fmt"
kapi "k8s.io/kubernetes/pkg/api"
kapierrors "k8s.io/kubernetes/pkg/api/errors"
"k8s.io/kubernetes/pkg/auth/user"
"k8s.io/kubernetes/pkg/runtime"
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
authorizationvalidation "github.com/openshift/origin/pkg/authorization/api/validation"
"github.com/openshift/origin/pkg/authorization/authorizer"
)
// REST implements the RESTStorage interface in terms of an Registry.
type REST struct {
authorizer authorizer.Authorizer
}
// NewREST creates a new REST for policies.
func NewREST(authorizer authorizer.Authorizer) *REST {
return &REST{authorizer}
}
// New creates a new ResourceAccessReview object
func (r *REST) New() runtime.Object {
return &authorizationapi.SubjectAccessReview{}
}
// Create registers a given new ResourceAccessReview instance to r.registry.
func (r *REST) Create(ctx kapi.Context, obj runtime.Object) (runtime.Object, error) {
subjectAccessReview, ok := obj.(*authorizationapi.SubjectAccessReview)
if !ok {
return nil, kapierrors.NewBadRequest(fmt.Sprintf("not a subjectAccessReview: %#v", obj))
}
if errs := authorizationvalidation.ValidateSubjectAccessReview(subjectAccessReview); len(errs) > 0 {
return nil, kapierrors.NewInvalid(authorizationapi.Kind(subjectAccessReview.Kind), "", errs)
}
// if a namespace is present on the request, then the namespace on the on the SAR is overwritten.
// This is to support backwards compatibility. To have gotten here in this state, it means that
// the authorizer decided that a user could run an SAR against this namespace
if namespace := kapi.NamespaceValue(ctx); len(namespace) > 0 {
subjectAccessReview.Action.Namespace = namespace
} else if err := r.isAllowed(ctx, subjectAccessReview); err != nil {
// this check is mutually exclusive to the condition above. localSAR and localRAR both clear the namespace before delegating their calls
// We only need to check if the SAR is allowed **again** if the authorizer didn't already approve the request for a legacy call.
return nil, err
}
var userToCheck *user.DefaultInfo
if (len(subjectAccessReview.User) == 0) && (len(subjectAccessReview.Groups) == 0) {
// if no user or group was specified, use the info from the context
ctxUser, exists := kapi.UserFrom(ctx)
if !exists {
return nil, kapierrors.NewBadRequest("user missing from context")
}
// make a copy, we don't want to risk changing the original
newExtra := map[string][]string{}
for k, v := range ctxUser.GetExtra() {
if v == nil {
newExtra[k] = nil
continue
}
newSlice := make([]string, len(v), len(v))
copy(newSlice, v)
newExtra[k] = newSlice
}
userToCheck = &user.DefaultInfo{
Name: ctxUser.GetName(),
Groups: ctxUser.GetGroups(),
UID: ctxUser.GetUID(),
Extra: newExtra,
}
} else {
userToCheck = &user.DefaultInfo{
Name: subjectAccessReview.User,
Groups: subjectAccessReview.Groups.List(),
Extra: map[string][]string{},
}
}
switch {
case subjectAccessReview.Scopes == nil:
// leave the scopes alone. on a self-sar, this means "use incoming request", on regular-sar it means, "use no scope restrictions"
case len(subjectAccessReview.Scopes) == 0:
// this always means "use no scope restrictions", so delete them
delete(userToCheck.Extra, authorizationapi.ScopesKey)
case len(subjectAccessReview.Scopes) > 0:
// this always means, "use these scope restrictions", so force the value
userToCheck.Extra[authorizationapi.ScopesKey] = subjectAccessReview.Scopes
}
requestContext := kapi.WithNamespace(kapi.WithUser(ctx, userToCheck), subjectAccessReview.Action.Namespace)
attributes := authorizer.ToDefaultAuthorizationAttributes(subjectAccessReview.Action)
allowed, reason, err := r.authorizer.Authorize(requestContext, attributes)
if err != nil {
return nil, err
}
response := &authorizationapi.SubjectAccessReviewResponse{
Namespace: subjectAccessReview.Action.Namespace,
Allowed: allowed,
Reason: reason,
}
return response, nil
}
// isAllowed checks to see if the current user has rights to issue a LocalSubjectAccessReview on the namespace they're attempting to access
func (r *REST) isAllowed(ctx kapi.Context, sar *authorizationapi.SubjectAccessReview) error {
localSARAttributes := authorizer.DefaultAuthorizationAttributes{
Verb: "create",
Resource: "localsubjectaccessreviews",
RequestAttributes: sar,
}
allowed, reason, err := r.authorizer.Authorize(kapi.WithNamespace(ctx, sar.Action.Namespace), localSARAttributes)
if err != nil {
return kapierrors.NewForbidden(authorizationapi.Resource(localSARAttributes.GetResource()), localSARAttributes.GetResourceName(), err)
}
if !allowed {
forbiddenError, _ := kapierrors.NewForbidden(authorizationapi.Resource(localSARAttributes.GetResource()), localSARAttributes.GetResourceName(), errors.New("") /*discarded*/).(*kapierrors.StatusError)
forbiddenError.ErrStatus.Message = reason
return forbiddenError
}
return nil
}