Add system:authenticated group
Jordan Liggitt authored on 2015/01/22 03:20:55... | ... |
@@ -25,3 +25,9 @@ type Assertion interface { |
25 | 25 |
type Client interface { |
26 | 26 |
AuthenticateClient(client api.Client) (api.UserInfo, bool, error) |
27 | 27 |
} |
28 |
+ |
|
29 |
+type RequestFunc func(req *http.Request) (api.UserInfo, bool, error) |
|
30 |
+ |
|
31 |
+func (f RequestFunc) AuthenticateRequest(req *http.Request) (api.UserInfo, bool, error) { |
|
32 |
+ return f(req) |
|
33 |
+} |
28 | 34 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,31 @@ |
0 |
+package bearertoken |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "net/http" |
|
4 |
+ "strings" |
|
5 |
+ |
|
6 |
+ "github.com/openshift/origin/pkg/auth/api" |
|
7 |
+ "github.com/openshift/origin/pkg/auth/authenticator" |
|
8 |
+) |
|
9 |
+ |
|
10 |
+type Authenticator struct { |
|
11 |
+ auth authenticator.Token |
|
12 |
+} |
|
13 |
+ |
|
14 |
+func New(auth authenticator.Token) *Authenticator { |
|
15 |
+ return &Authenticator{auth} |
|
16 |
+} |
|
17 |
+ |
|
18 |
+func (a *Authenticator) AuthenticateRequest(req *http.Request) (api.UserInfo, bool, error) { |
|
19 |
+ auth := strings.TrimSpace(req.Header.Get("Authorization")) |
|
20 |
+ if auth == "" { |
|
21 |
+ return nil, false, nil |
|
22 |
+ } |
|
23 |
+ parts := strings.Split(auth, " ") |
|
24 |
+ if len(parts) < 2 || strings.ToLower(parts[0]) != "bearer" { |
|
25 |
+ return nil, false, nil |
|
26 |
+ } |
|
27 |
+ |
|
28 |
+ token := parts[1] |
|
29 |
+ return a.auth.AuthenticateToken(token) |
|
30 |
+} |
0 | 31 |
deleted file mode 100644 |
... | ... |
@@ -1,31 +0,0 @@ |
1 |
-package bearertoken |
|
2 |
- |
|
3 |
-import ( |
|
4 |
- "net/http" |
|
5 |
- "strings" |
|
6 |
- |
|
7 |
- "github.com/openshift/origin/pkg/auth/api" |
|
8 |
- "github.com/openshift/origin/pkg/auth/authenticator" |
|
9 |
-) |
|
10 |
- |
|
11 |
-type Authenticator struct { |
|
12 |
- auth authenticator.Token |
|
13 |
-} |
|
14 |
- |
|
15 |
-func New(auth authenticator.Token) *Authenticator { |
|
16 |
- return &Authenticator{auth} |
|
17 |
-} |
|
18 |
- |
|
19 |
-func (a *Authenticator) AuthenticateRequest(req *http.Request) (api.UserInfo, bool, error) { |
|
20 |
- auth := strings.TrimSpace(req.Header.Get("Authorization")) |
|
21 |
- if auth == "" { |
|
22 |
- return nil, false, nil |
|
23 |
- } |
|
24 |
- parts := strings.Split(auth, " ") |
|
25 |
- if len(parts) < 2 || strings.ToLower(parts[0]) != "bearer" { |
|
26 |
- return nil, false, nil |
|
27 |
- } |
|
28 |
- |
|
29 |
- token := parts[1] |
|
30 |
- return a.auth.AuthenticateToken(token) |
|
31 |
-} |
32 | 1 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,32 @@ |
0 |
+package group |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "net/http" |
|
4 |
+ |
|
5 |
+ "github.com/openshift/origin/pkg/auth/api" |
|
6 |
+ "github.com/openshift/origin/pkg/auth/authenticator" |
|
7 |
+) |
|
8 |
+ |
|
9 |
+// GroupAdder wraps a request authenticator, and adds the specified groups to the returned user when authentication succeeds |
|
10 |
+type GroupAdder struct { |
|
11 |
+ Authenticator authenticator.Request |
|
12 |
+ Groups []string |
|
13 |
+} |
|
14 |
+ |
|
15 |
+func (g *GroupAdder) AuthenticateRequest(req *http.Request) (api.UserInfo, bool, error) { |
|
16 |
+ user, ok, err := g.Authenticator.AuthenticateRequest(req) |
|
17 |
+ if err != nil || !ok { |
|
18 |
+ return nil, ok, err |
|
19 |
+ } |
|
20 |
+ return &api.DefaultUserInfo{ |
|
21 |
+ Name: user.GetName(), |
|
22 |
+ UID: user.GetUID(), |
|
23 |
+ Groups: append(user.GetGroups(), g.Groups...), |
|
24 |
+ Scope: user.GetScope(), |
|
25 |
+ Extra: user.GetExtra(), |
|
26 |
+ }, true, nil |
|
27 |
+} |
|
28 |
+ |
|
29 |
+func NewGroupAdder(auth authenticator.Request, groups []string) *GroupAdder { |
|
30 |
+ return &GroupAdder{auth, groups} |
|
31 |
+} |
0 | 32 |
new file mode 100644 |
... | ... |
@@ -0,0 +1,26 @@ |
0 |
+package group |
|
1 |
+ |
|
2 |
+import ( |
|
3 |
+ "net/http" |
|
4 |
+ "reflect" |
|
5 |
+ "testing" |
|
6 |
+ |
|
7 |
+ "github.com/openshift/origin/pkg/auth/api" |
|
8 |
+ "github.com/openshift/origin/pkg/auth/authenticator" |
|
9 |
+) |
|
10 |
+ |
|
11 |
+func TestGroupAdder(t *testing.T) { |
|
12 |
+ adder := authenticator.Request( |
|
13 |
+ NewGroupAdder( |
|
14 |
+ authenticator.RequestFunc(func(req *http.Request) (api.UserInfo, bool, error) { |
|
15 |
+ return &api.DefaultUserInfo{Name: "user", Groups: []string{"original"}}, true, nil |
|
16 |
+ }), |
|
17 |
+ []string{"added"}, |
|
18 |
+ ), |
|
19 |
+ ) |
|
20 |
+ |
|
21 |
+ user, _, _ := adder.AuthenticateRequest(nil) |
|
22 |
+ if !reflect.DeepEqual(user.GetGroups(), []string{"original", "added"}) { |
|
23 |
+ t.Errorf("Expected original,added groups, got %#v", user.GetGroups()) |
|
24 |
+ } |
|
25 |
+} |
... | ... |
@@ -22,9 +22,9 @@ import ( |
22 | 22 |
"github.com/openshift/origin/pkg/auth/authenticator/password/allowanypassword" |
23 | 23 |
"github.com/openshift/origin/pkg/auth/authenticator/password/basicauthpassword" |
24 | 24 |
"github.com/openshift/origin/pkg/auth/authenticator/request/basicauthrequest" |
25 |
+ "github.com/openshift/origin/pkg/auth/authenticator/request/bearertoken" |
|
25 | 26 |
"github.com/openshift/origin/pkg/auth/authenticator/request/headerrequest" |
26 | 27 |
"github.com/openshift/origin/pkg/auth/authenticator/request/unionrequest" |
27 |
- "github.com/openshift/origin/pkg/auth/authenticator/token/bearertoken" |
|
28 | 28 |
"github.com/openshift/origin/pkg/auth/authenticator/token/filetoken" |
29 | 29 |
"github.com/openshift/origin/pkg/auth/oauth/external" |
30 | 30 |
"github.com/openshift/origin/pkg/auth/oauth/external/github" |
... | ... |
@@ -391,13 +391,9 @@ func wrapHandlerWithAuthentication(handler http.Handler, authenticator authentic |
391 | 391 |
requestsToUsers, |
392 | 392 |
authenticator, |
393 | 393 |
http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { |
394 |
- // TODO: make this failure handler actually fail once internal components can get auth tokens to do their job |
|
395 |
- // w.WriteHeader(http.StatusUnauthorized) |
|
396 |
- // return |
|
397 |
- |
|
398 |
- // For now, just let us know and continue on your merry way |
|
399 |
- glog.V(2).Infof("Token authentication failed when accessing: %v", req.URL) |
|
400 |
- handler.ServeHTTP(w, req) |
|
394 |
+ w.WriteHeader(http.StatusUnauthorized) |
|
395 |
+ w.Write([]byte("Unauthorized")) |
|
396 |
+ return |
|
401 | 397 |
}), |
402 | 398 |
handler) |
403 | 399 |
} |
... | ... |
@@ -27,7 +27,12 @@ import ( |
27 | 27 |
"github.com/spf13/cobra" |
28 | 28 |
|
29 | 29 |
"github.com/openshift/origin/pkg/api/latest" |
30 |
- "github.com/openshift/origin/pkg/auth/authenticator/token/bearertoken" |
|
30 |
+ "github.com/openshift/origin/pkg/auth/api" |
|
31 |
+ "github.com/openshift/origin/pkg/auth/authenticator" |
|
32 |
+ "github.com/openshift/origin/pkg/auth/authenticator/request/bearertoken" |
|
33 |
+ "github.com/openshift/origin/pkg/auth/authenticator/request/unionrequest" |
|
34 |
+ "github.com/openshift/origin/pkg/auth/authenticator/request/x509request" |
|
35 |
+ "github.com/openshift/origin/pkg/auth/group" |
|
31 | 36 |
"github.com/openshift/origin/pkg/cmd/flagtypes" |
32 | 37 |
"github.com/openshift/origin/pkg/cmd/server/crypto" |
33 | 38 |
"github.com/openshift/origin/pkg/cmd/server/etcd" |
... | ... |
@@ -69,6 +74,13 @@ You may also pass --etcd to connect to an external etcd server instead of runnin |
69 | 69 |
instance. |
70 | 70 |
` |
71 | 71 |
|
72 |
+const ( |
|
73 |
+ unauthenticatedUsername = "system:anonymous" |
|
74 |
+ |
|
75 |
+ authenticatedGroup = "system:authenticated" |
|
76 |
+ unauthenticatedGroup = "system:unauthenticated" |
|
77 |
+) |
|
78 |
+ |
|
72 | 79 |
// config is a struct that the command stores flag values into. |
73 | 80 |
type config struct { |
74 | 81 |
Docker *docker.Helper |
... | ... |
@@ -299,11 +311,12 @@ func start(cfg *config, args []string) error { |
299 | 299 |
} |
300 | 300 |
|
301 | 301 |
// Build token auth for user's OAuth tokens |
302 |
+ authenticators := []authenticator.Request{} |
|
302 | 303 |
tokenAuthenticator, err := origin.GetTokenAuthenticator(etcdHelper) |
303 | 304 |
if err != nil { |
304 | 305 |
glog.Fatalf("Error creating TokenAuthenticator: %v", err) |
305 | 306 |
} |
306 |
- osmaster.Authenticator = bearertoken.New(tokenAuthenticator) |
|
307 |
+ authenticators = append(authenticators, group.NewGroupAdder(bearertoken.New(tokenAuthenticator), []string{authenticatedGroup})) |
|
307 | 308 |
|
308 | 309 |
var roots *x509.CertPool |
309 | 310 |
if osmaster.TLS { |
... | ... |
@@ -355,6 +368,14 @@ func start(cfg *config, args []string) error { |
355 | 355 |
for _, root := range ca.Config.Roots { |
356 | 356 |
roots.AddCert(root) |
357 | 357 |
} |
358 |
+ |
|
359 |
+ // build cert authenticator |
|
360 |
+ // TODO: add cert users to etcd? |
|
361 |
+ // TODO: provider-qualify cert users? |
|
362 |
+ opts := x509request.DefaultVerifyOptions() |
|
363 |
+ opts.Roots = roots |
|
364 |
+ certauth := x509request.New(opts, x509request.CommonNameUserConversion) |
|
365 |
+ authenticators = append(authenticators, group.NewGroupAdder(certauth, []string{authenticatedGroup})) |
|
358 | 366 |
} else { |
359 | 367 |
// No security, use the same client config for all OpenShift clients |
360 | 368 |
osClientConfig := kclient.Config{Host: cfg.MasterAddr.URL.String(), Version: latest.Version} |
... | ... |
@@ -362,6 +383,13 @@ func start(cfg *config, args []string) error { |
362 | 362 |
osmaster.DeployerOSClientConfig = osClientConfig |
363 | 363 |
} |
364 | 364 |
|
365 |
+ // TODO: make anonymous auth optional? |
|
366 |
+ // TODO: should this map to a real user persisted in etcd? |
|
367 |
+ authenticators = append(authenticators, authenticator.RequestFunc(func(req *http.Request) (api.UserInfo, bool, error) { |
|
368 |
+ return &api.DefaultUserInfo{Name: unauthenticatedUsername, Groups: []string{unauthenticatedGroup}}, true, nil |
|
369 |
+ })) |
|
370 |
+ osmaster.Authenticator = unionrequest.NewUnionAuthentication(authenticators) |
|
371 |
+ |
|
365 | 372 |
osmaster.BuildClients() |
366 | 373 |
osmaster.EnsureCORSAllowedOrigins(cfg.CORSAllowedOrigins) |
367 | 374 |
|
... | ... |
@@ -35,7 +35,7 @@ func NewCurrentContextFilter(requestPath string, context Context, handler http.H |
35 | 35 |
|
36 | 36 |
user, found := context.Get(req) |
37 | 37 |
if !found { |
38 |
- http.Error(w, "Need to be authorized to access this method", http.StatusUnauthorized) |
|
38 |
+ http.Error(w, "Need to be authenticated to access this method", http.StatusUnauthorized) |
|
39 | 39 |
return |
40 | 40 |
} |
41 | 41 |
|
... | ... |
@@ -51,7 +51,7 @@ func InstallThisUser(mux mux, endpoint string, requestsToUsers Context, apiHandl |
51 | 51 |
func(w http.ResponseWriter, req *http.Request) { |
52 | 52 |
user, found := requestsToUsers.Get(req) |
53 | 53 |
if !found { |
54 |
- http.Error(w, "Need to be authorized to access this method", http.StatusUnauthorized) |
|
54 |
+ http.Error(w, "Need to be authenticated to access this method", http.StatusUnauthorized) |
|
55 | 55 |
return |
56 | 56 |
} |
57 | 57 |
|