Skip to content

Commit c0e66f9

Browse files
Move quasi-generic handlers out so controller can reuse them
Will be replaced in a future release with generic upstream filters.
1 parent 4fb5fa6 commit c0e66f9

File tree

6 files changed

+281
-240
lines changed

6 files changed

+281
-240
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package handlers
2+
3+
import (
4+
"net/http"
5+
6+
"github.com/golang/glog"
7+
8+
kapi "k8s.io/kubernetes/pkg/api"
9+
"k8s.io/kubernetes/pkg/auth/authenticator"
10+
)
11+
12+
// AuthenticationHandlerFilter creates a filter object that will enforce authentication directly
13+
func AuthenticationHandlerFilter(handler http.Handler, authenticator authenticator.Request, contextMapper kapi.RequestContextMapper) http.Handler {
14+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
15+
user, ok, err := authenticator.AuthenticateRequest(req)
16+
if err != nil || !ok {
17+
http.Error(w, "Unauthorized", http.StatusUnauthorized)
18+
return
19+
}
20+
21+
ctx, ok := contextMapper.Get(req)
22+
if !ok {
23+
http.Error(w, "Unable to find request context", http.StatusInternalServerError)
24+
return
25+
}
26+
if err := contextMapper.Update(req, kapi.WithUser(ctx, user)); err != nil {
27+
glog.V(4).Infof("Error setting authenticated context: %v", err)
28+
http.Error(w, "Unable to set authenticated request context", http.StatusInternalServerError)
29+
return
30+
}
31+
32+
handler.ServeHTTP(w, req)
33+
})
34+
}
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
package handlers
2+
3+
import (
4+
"bytes"
5+
"encoding/json"
6+
"errors"
7+
"fmt"
8+
"net/http"
9+
10+
restful "github.com/emicklei/go-restful"
11+
12+
kapi "k8s.io/kubernetes/pkg/api"
13+
kapierrors "k8s.io/kubernetes/pkg/api/errors"
14+
"k8s.io/kubernetes/pkg/api/unversioned"
15+
"k8s.io/kubernetes/pkg/runtime"
16+
17+
"github.com/openshift/origin/pkg/authorization/authorizer"
18+
)
19+
20+
// AuthorizationFilter imposes normal authorization rules
21+
func AuthorizationFilter(handler http.Handler, authorizer authorizer.Authorizer, authorizationAttributeBuilder authorizer.AuthorizationAttributeBuilder, contextMapper kapi.RequestContextMapper) http.Handler {
22+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
23+
attributes, err := authorizationAttributeBuilder.GetAttributes(req)
24+
if err != nil {
25+
Forbidden(err.Error(), attributes, w, req)
26+
return
27+
}
28+
if attributes == nil {
29+
Forbidden("No attributes", attributes, w, req)
30+
return
31+
}
32+
33+
ctx, exists := contextMapper.Get(req)
34+
if !exists {
35+
Forbidden("context not found", attributes, w, req)
36+
return
37+
}
38+
39+
allowed, reason, err := authorizer.Authorize(ctx, attributes)
40+
if err != nil {
41+
Forbidden(err.Error(), attributes, w, req)
42+
return
43+
}
44+
if !allowed {
45+
Forbidden(reason, attributes, w, req)
46+
return
47+
}
48+
49+
handler.ServeHTTP(w, req)
50+
})
51+
}
52+
53+
// Forbidden renders a simple forbidden error to the response
54+
func Forbidden(reason string, attributes authorizer.Action, w http.ResponseWriter, req *http.Request) {
55+
kind := ""
56+
resource := ""
57+
group := ""
58+
name := ""
59+
// the attributes can be empty for two basic reasons:
60+
// 1. malformed API request
61+
// 2. not an API request at all
62+
// In these cases, just assume default that will work better than nothing
63+
if attributes != nil {
64+
group = attributes.GetAPIGroup()
65+
resource = attributes.GetResource()
66+
kind = attributes.GetResource()
67+
if len(attributes.GetAPIGroup()) > 0 {
68+
kind = attributes.GetAPIGroup() + "." + kind
69+
}
70+
name = attributes.GetResourceName()
71+
}
72+
73+
// Reason is an opaque string that describes why access is allowed or forbidden (forbidden by the time we reach here).
74+
// We don't have direct access to kind or name (not that those apply either in the general case)
75+
// We create a NewForbidden to stay close the API, but then we override the message to get a serialization
76+
// that makes sense when a human reads it.
77+
forbiddenError := kapierrors.NewForbidden(unversioned.GroupResource{Group: group, Resource: resource}, name, errors.New("") /*discarded*/)
78+
forbiddenError.ErrStatus.Message = reason
79+
80+
formatted := &bytes.Buffer{}
81+
output, err := runtime.Encode(kapi.Codecs.LegacyCodec(kapi.SchemeGroupVersion), &forbiddenError.ErrStatus)
82+
if err != nil {
83+
fmt.Fprintf(formatted, "%s", forbiddenError.Error())
84+
} else {
85+
json.Indent(formatted, output, "", " ")
86+
}
87+
88+
w.Header().Set("Content-Type", restful.MIME_JSON)
89+
w.WriteHeader(http.StatusForbidden)
90+
w.Write(formatted.Bytes())
91+
}
Lines changed: 142 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,142 @@
1+
package handlers
2+
3+
import (
4+
"fmt"
5+
"net/http"
6+
7+
kapi "k8s.io/kubernetes/pkg/api"
8+
"k8s.io/kubernetes/pkg/auth/user"
9+
"k8s.io/kubernetes/pkg/httplog"
10+
"k8s.io/kubernetes/pkg/serviceaccount"
11+
12+
authenticationapi "github.com/openshift/origin/pkg/auth/api"
13+
authorizationapi "github.com/openshift/origin/pkg/authorization/api"
14+
"github.com/openshift/origin/pkg/authorization/authorizer"
15+
"github.com/openshift/origin/pkg/cmd/server/bootstrappolicy"
16+
userapi "github.com/openshift/origin/pkg/user/api"
17+
uservalidation "github.com/openshift/origin/pkg/user/api/validation"
18+
)
19+
20+
type GroupCache interface {
21+
GroupsFor(string) ([]*userapi.Group, error)
22+
}
23+
24+
// ImpersonationFilter checks for impersonation rules against the current user.
25+
func ImpersonationFilter(handler http.Handler, a authorizer.Authorizer, groupCache GroupCache, contextMapper kapi.RequestContextMapper) http.Handler {
26+
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
27+
requestedUser := req.Header.Get(authenticationapi.ImpersonateUserHeader)
28+
if len(requestedUser) == 0 {
29+
handler.ServeHTTP(w, req)
30+
return
31+
}
32+
33+
subjects := authorizationapi.BuildSubjects([]string{requestedUser}, req.Header[authenticationapi.ImpersonateGroupHeader],
34+
// validates whether the usernames are regular users or system users
35+
uservalidation.ValidateUserName,
36+
// validates group names are regular groups or system groups
37+
uservalidation.ValidateGroupName)
38+
39+
ctx, exists := contextMapper.Get(req)
40+
if !exists {
41+
Forbidden("context not found", nil, w, req)
42+
return
43+
}
44+
45+
// if groups are not specified, then we need to look them up differently depending on the type of user
46+
// if they are specified, then they are the authority
47+
groupsSpecified := len(req.Header[authenticationapi.ImpersonateGroupHeader]) > 0
48+
49+
// make sure we're allowed to impersonate each subject. While we're iterating through, start building username
50+
// and group information
51+
username := ""
52+
groups := []string{}
53+
for _, subject := range subjects {
54+
actingAsAttributes := &authorizer.DefaultAuthorizationAttributes{
55+
Verb: "impersonate",
56+
}
57+
58+
switch subject.GetObjectKind().GroupVersionKind().GroupKind() {
59+
case userapi.Kind(authorizationapi.GroupKind):
60+
actingAsAttributes.APIGroup = userapi.GroupName
61+
actingAsAttributes.Resource = authorizationapi.GroupResource
62+
actingAsAttributes.ResourceName = subject.Name
63+
groups = append(groups, subject.Name)
64+
65+
case userapi.Kind(authorizationapi.SystemGroupKind):
66+
actingAsAttributes.APIGroup = userapi.GroupName
67+
actingAsAttributes.Resource = authorizationapi.SystemGroupResource
68+
actingAsAttributes.ResourceName = subject.Name
69+
groups = append(groups, subject.Name)
70+
71+
case userapi.Kind(authorizationapi.UserKind):
72+
actingAsAttributes.APIGroup = userapi.GroupName
73+
actingAsAttributes.Resource = authorizationapi.UserResource
74+
actingAsAttributes.ResourceName = subject.Name
75+
username = subject.Name
76+
if !groupsSpecified {
77+
if actualGroups, err := groupCache.GroupsFor(subject.Name); err == nil {
78+
for _, group := range actualGroups {
79+
groups = append(groups, group.Name)
80+
}
81+
}
82+
groups = append(groups, bootstrappolicy.AuthenticatedGroup, bootstrappolicy.AuthenticatedOAuthGroup)
83+
}
84+
85+
case userapi.Kind(authorizationapi.SystemUserKind):
86+
actingAsAttributes.APIGroup = userapi.GroupName
87+
actingAsAttributes.Resource = authorizationapi.SystemUserResource
88+
actingAsAttributes.ResourceName = subject.Name
89+
username = subject.Name
90+
if !groupsSpecified {
91+
if subject.Name == bootstrappolicy.UnauthenticatedUsername {
92+
groups = append(groups, bootstrappolicy.UnauthenticatedGroup)
93+
} else {
94+
groups = append(groups, bootstrappolicy.AuthenticatedGroup)
95+
}
96+
}
97+
98+
case kapi.Kind(authorizationapi.ServiceAccountKind):
99+
actingAsAttributes.APIGroup = kapi.GroupName
100+
actingAsAttributes.Resource = authorizationapi.ServiceAccountResource
101+
actingAsAttributes.ResourceName = subject.Name
102+
username = serviceaccount.MakeUsername(subject.Namespace, subject.Name)
103+
if !groupsSpecified {
104+
groups = append(serviceaccount.MakeGroupNames(subject.Namespace, subject.Name), bootstrappolicy.AuthenticatedGroup)
105+
}
106+
107+
default:
108+
Forbidden(fmt.Sprintf("unknown subject type: %v", subject), actingAsAttributes, w, req)
109+
return
110+
}
111+
112+
authCheckCtx := kapi.WithNamespace(ctx, subject.Namespace)
113+
114+
allowed, reason, err := a.Authorize(authCheckCtx, actingAsAttributes)
115+
if err != nil {
116+
Forbidden(err.Error(), actingAsAttributes, w, req)
117+
return
118+
}
119+
if !allowed {
120+
Forbidden(reason, actingAsAttributes, w, req)
121+
return
122+
}
123+
}
124+
125+
var extra map[string][]string
126+
if requestScopes, ok := req.Header[authenticationapi.ImpersonateUserScopeHeader]; ok {
127+
extra = map[string][]string{authorizationapi.ScopesKey: requestScopes}
128+
}
129+
130+
newUser := &user.DefaultInfo{
131+
Name: username,
132+
Groups: groups,
133+
Extra: extra,
134+
}
135+
contextMapper.Update(req, kapi.WithUser(ctx, newUser))
136+
137+
oldUser, _ := kapi.UserFrom(ctx)
138+
httplog.LogOf(req, w).Addf("%v is acting as %v", oldUser, newUser)
139+
140+
handler.ServeHTTP(w, req)
141+
})
142+
}

pkg/cmd/server/origin/auth.go

Lines changed: 0 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -762,27 +762,3 @@ func (redirectSuccessHandler) AuthenticationSucceeded(user kuser.Info, then stri
762762
http.Redirect(w, req, then, http.StatusFound)
763763
return true, nil
764764
}
765-
766-
// authenticationHandlerFilter creates a filter object that will enforce authentication directly
767-
func authenticationHandlerFilter(handler http.Handler, authenticator authenticator.Request, contextMapper kapi.RequestContextMapper) http.Handler {
768-
return http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) {
769-
user, ok, err := authenticator.AuthenticateRequest(req)
770-
if err != nil || !ok {
771-
http.Error(w, "Unauthorized", http.StatusUnauthorized)
772-
return
773-
}
774-
775-
ctx, ok := contextMapper.Get(req)
776-
if !ok {
777-
http.Error(w, "Unable to find request context", http.StatusInternalServerError)
778-
return
779-
}
780-
if err := contextMapper.Update(req, kapi.WithUser(ctx, user)); err != nil {
781-
glog.V(4).Infof("Error setting authenticated context: %v", err)
782-
http.Error(w, "Unable to set authenticated request context", http.StatusInternalServerError)
783-
return
784-
}
785-
786-
handler.ServeHTTP(w, req)
787-
})
788-
}

0 commit comments

Comments
 (0)