Browse code

Initial group support

Jordan Liggitt authored on 2015/04/10 15:38:49
Showing 10 changed files
... ...
@@ -19,6 +19,8 @@ import (
19 19
 	"github.com/openshift/origin/pkg/oauth/registry/test"
20 20
 	"github.com/openshift/origin/pkg/oauth/server/osinserver"
21 21
 	"github.com/openshift/origin/pkg/oauth/server/osinserver/registrystorage"
22
+	userapi "github.com/openshift/origin/pkg/user/api"
23
+	usertest "github.com/openshift/origin/pkg/user/registry/test"
22 24
 )
23 25
 
24 26
 type testHandlers struct {
... ...
@@ -245,7 +247,8 @@ func TestRegistryAndServer(t *testing.T) {
245 245
 
246 246
 func TestAuthenticateTokenNotFound(t *testing.T) {
247 247
 	tokenRegistry := &test.AccessTokenRegistry{Err: apierrs.NewNotFound("AccessToken", "token")}
248
-	tokenAuthenticator := NewTokenAuthenticator(tokenRegistry)
248
+	userRegistry := usertest.NewUserRegistry()
249
+	tokenAuthenticator := NewTokenAuthenticator(tokenRegistry, userRegistry)
249 250
 
250 251
 	userInfo, found, err := tokenAuthenticator.AuthenticateToken("token")
251 252
 	if found {
... ...
@@ -263,7 +266,8 @@ func TestAuthenticateTokenNotFound(t *testing.T) {
263 263
 }
264 264
 func TestAuthenticateTokenOtherGetError(t *testing.T) {
265 265
 	tokenRegistry := &test.AccessTokenRegistry{Err: errors.New("get error")}
266
-	tokenAuthenticator := NewTokenAuthenticator(tokenRegistry)
266
+	userRegistry := usertest.NewUserRegistry()
267
+	tokenAuthenticator := NewTokenAuthenticator(tokenRegistry, userRegistry)
267 268
 
268 269
 	userInfo, found, err := tokenAuthenticator.AuthenticateToken("token")
269 270
 	if found {
... ...
@@ -287,7 +291,8 @@ func TestAuthenticateTokenExpired(t *testing.T) {
287 287
 			ExpiresIn:  600, // 10 minutes
288 288
 		},
289 289
 	}
290
-	tokenAuthenticator := NewTokenAuthenticator(tokenRegistry)
290
+	userRegistry := usertest.NewUserRegistry()
291
+	tokenAuthenticator := NewTokenAuthenticator(tokenRegistry, userRegistry)
291 292
 
292 293
 	userInfo, found, err := tokenAuthenticator.AuthenticateToken("token")
293 294
 	if found {
... ...
@@ -306,9 +311,14 @@ func TestAuthenticateTokenValidated(t *testing.T) {
306 306
 		AccessToken: &oapi.OAuthAccessToken{
307 307
 			ObjectMeta: kapi.ObjectMeta{CreationTimestamp: util.Time{time.Now()}},
308 308
 			ExpiresIn:  600, // 10 minutes
309
+			UserName:   "foo",
310
+			UserUID:    string("bar"),
309 311
 		},
310 312
 	}
311
-	tokenAuthenticator := NewTokenAuthenticator(tokenRegistry)
313
+	userRegistry := usertest.NewUserRegistry()
314
+	userRegistry.Get["foo"] = &userapi.User{ObjectMeta: kapi.ObjectMeta{UID: "bar"}}
315
+
316
+	tokenAuthenticator := NewTokenAuthenticator(tokenRegistry, userRegistry)
312 317
 
313 318
 	userInfo, found, err := tokenAuthenticator.AuthenticateToken("token")
314 319
 	if !found {
... ...
@@ -2,35 +2,51 @@ package registry
2 2
 
3 3
 import (
4 4
 	"errors"
5
+	"fmt"
5 6
 	"time"
6 7
 
7 8
 	"github.com/GoogleCloudPlatform/kubernetes/pkg/api"
8
-	"github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
9
+	kuser "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
9 10
 	"github.com/openshift/origin/pkg/oauth/registry/oauthaccesstoken"
11
+	"github.com/openshift/origin/pkg/user/registry/user"
10 12
 )
11 13
 
12 14
 type TokenAuthenticator struct {
13
-	registry oauthaccesstoken.Registry
15
+	tokens oauthaccesstoken.Registry
16
+	users  user.Registry
14 17
 }
15 18
 
16 19
 var ErrExpired = errors.New("Token is expired")
17 20
 
18
-func NewTokenAuthenticator(registry oauthaccesstoken.Registry) *TokenAuthenticator {
21
+func NewTokenAuthenticator(tokens oauthaccesstoken.Registry, users user.Registry) *TokenAuthenticator {
19 22
 	return &TokenAuthenticator{
20
-		registry: registry,
23
+		tokens: tokens,
24
+		users:  users,
21 25
 	}
22 26
 }
23 27
 
24
-func (a *TokenAuthenticator) AuthenticateToken(value string) (user.Info, bool, error) {
25
-	token, err := a.registry.GetAccessToken(api.NewContext(), value)
28
+func (a *TokenAuthenticator) AuthenticateToken(value string) (kuser.Info, bool, error) {
29
+	ctx := api.NewContext()
30
+
31
+	token, err := a.tokens.GetAccessToken(ctx, value)
26 32
 	if err != nil {
27 33
 		return nil, false, err
28 34
 	}
29 35
 	if token.CreationTimestamp.Time.Add(time.Duration(token.ExpiresIn) * time.Second).Before(time.Now()) {
30 36
 		return nil, false, ErrExpired
31 37
 	}
32
-	return &user.DefaultInfo{
33
-		Name: token.UserName,
34
-		UID:  token.UserUID,
38
+
39
+	u, err := a.users.GetUser(ctx, token.UserName)
40
+	if err != nil {
41
+		return nil, false, err
42
+	}
43
+	if string(u.UID) != token.UserUID {
44
+		return nil, false, fmt.Errorf("user.UID (%s) does not match token.userUID (%s)", u.UID, token.UserUID)
45
+	}
46
+
47
+	return &kuser.DefaultInfo{
48
+		Name:   u.Name,
49
+		UID:    string(u.UID),
50
+		Groups: u.Groups,
35 51
 	}, true, nil
36 52
 }
... ...
@@ -5,27 +5,37 @@ import (
5 5
 	kuser "github.com/GoogleCloudPlatform/kubernetes/pkg/auth/user"
6 6
 
7 7
 	authapi "github.com/openshift/origin/pkg/auth/api"
8
+	"github.com/openshift/origin/pkg/user/registry/user"
8 9
 	"github.com/openshift/origin/pkg/user/registry/useridentitymapping"
9 10
 )
10 11
 
11 12
 type lookupIdentityMapper struct {
12
-	registry useridentitymapping.Registry
13
+	mappings useridentitymapping.Registry
14
+	users    user.Registry
13 15
 }
14 16
 
15 17
 // NewLookupIdentityMapper returns a mapper that will look up existing mappings for identities
16
-func NewLookupIdentityMapper(registry useridentitymapping.Registry) authapi.UserIdentityMapper {
17
-	return &lookupIdentityMapper{registry}
18
+func NewLookupIdentityMapper(mappings useridentitymapping.Registry, users user.Registry) authapi.UserIdentityMapper {
19
+	return &lookupIdentityMapper{mappings, users}
18 20
 }
19 21
 
20 22
 // UserFor returns info about the user for whom identity info has been provided
21 23
 func (p *lookupIdentityMapper) UserFor(info authapi.UserIdentityInfo) (kuser.Info, error) {
22
-	mapping, err := p.registry.GetUserIdentityMapping(kapi.NewContext(), info.GetIdentityName())
24
+	ctx := kapi.NewContext()
25
+
26
+	mapping, err := p.mappings.GetUserIdentityMapping(ctx, info.GetIdentityName())
27
+	if err != nil {
28
+		return nil, err
29
+	}
30
+
31
+	u, err := p.users.GetUser(ctx, mapping.User.Name)
23 32
 	if err != nil {
24 33
 		return nil, err
25 34
 	}
26 35
 
27 36
 	return &kuser.DefaultInfo{
28
-		Name: mapping.User.Name,
29
-		UID:  string(mapping.User.UID),
37
+		Name:   u.Name,
38
+		UID:    string(u.UID),
39
+		Groups: u.Groups,
30 40
 	}, nil
31 41
 }
... ...
@@ -112,8 +112,9 @@ func (p *provisioningIdentityMapper) createIdentityAndMapping(ctx kapi.Context,
112 112
 	}
113 113
 
114 114
 	return &kuser.DefaultInfo{
115
-		Name: persistedUser.Name,
116
-		UID:  string(persistedUser.UID),
115
+		Name:   persistedUser.Name,
116
+		UID:    string(persistedUser.UID),
117
+		Groups: persistedUser.Groups,
117 118
 	}, nil
118 119
 }
119 120
 
... ...
@@ -182,8 +183,9 @@ func (p *provisioningIdentityMapper) getMapping(ctx kapi.Context, identity *user
182 182
 		return nil, kerrs.NewNotFound("UserIdentityMapping", identity.Name)
183 183
 	}
184 184
 	return &kuser.DefaultInfo{
185
-		Name: u.Name,
186
-		UID:  string(u.UID),
185
+		Name:   u.Name,
186
+		UID:    string(u.UID),
187
+		Groups: u.Groups,
187 188
 	}, nil
188 189
 }
189 190
 
... ...
@@ -41,6 +41,8 @@ import (
41 41
 	accesstokenregistry "github.com/openshift/origin/pkg/oauth/registry/oauthaccesstoken"
42 42
 	accesstokenetcd "github.com/openshift/origin/pkg/oauth/registry/oauthaccesstoken/etcd"
43 43
 	projectauth "github.com/openshift/origin/pkg/project/auth"
44
+	userregistry "github.com/openshift/origin/pkg/user/registry/user"
45
+	useretcd "github.com/openshift/origin/pkg/user/registry/user/etcd"
44 46
 )
45 47
 
46 48
 const (
... ...
@@ -181,12 +183,12 @@ func newAuthenticator(servingInfo configapi.ServingInfo, etcdHelper tools.EtcdHe
181 181
 	// Allow token as access_token param for WebSockets
182 182
 	// TODO: make the param name configurable
183 183
 	// TODO: limit this authenticator to watch methods, if possible
184
-	// TODO: prevent access_token param from getting logged, if possible
185 184
 	authenticators = append(authenticators, paramtoken.New("access_token", tokenAuthenticator, true))
186 185
 
187 186
 	if configapi.UseTLS(servingInfo) {
188 187
 		// build cert authenticator
189
-		// TODO: add cert users to etcd?
188
+		// TODO: add "system:" prefix in authenticator, limit cert to username
189
+		// TODO: add "system:" prefix to groups in authenticator, limit cert to group name
190 190
 		opts := x509request.DefaultVerifyOptions()
191 191
 		opts.Roots = apiClientCAs
192 192
 		certauth := x509request.New(opts, x509request.SubjectToUserConversion)
... ...
@@ -240,7 +242,11 @@ func newAuthorizationAttributeBuilder(requestContextMapper kapi.RequestContextMa
240 240
 func getEtcdTokenAuthenticator(etcdHelper tools.EtcdHelper) authenticator.Token {
241 241
 	accessTokenStorage := accesstokenetcd.NewREST(etcdHelper)
242 242
 	accessTokenRegistry := accesstokenregistry.NewRegistry(accessTokenStorage)
243
-	return authnregistry.NewTokenAuthenticator(accessTokenRegistry)
243
+
244
+	userStorage := useretcd.NewREST(etcdHelper)
245
+	userRegistry := userregistry.NewRegistry(userStorage)
246
+
247
+	return authnregistry.NewTokenAuthenticator(accessTokenRegistry, userRegistry)
244 248
 }
245 249
 
246 250
 // KubeClient returns the kubernetes client object
... ...
@@ -12,6 +12,8 @@ type User struct {
12 12
 	FullName string
13 13
 
14 14
 	Identities []string
15
+
16
+	Groups []string
15 17
 }
16 18
 
17 19
 type UserList struct {
... ...
@@ -12,6 +12,8 @@ type User struct {
12 12
 	FullName string `json:"fullName,omitempty"`
13 13
 
14 14
 	Identities []string `json:"identities"`
15
+
16
+	Groups []string `json:"groups"`
15 17
 }
16 18
 
17 19
 type UserList struct {
... ...
@@ -12,6 +12,8 @@ type User struct {
12 12
 	FullName string `json:"fullName,omitempty"`
13 13
 
14 14
 	Identities []string `json:"identities"`
15
+
16
+	Groups []string `json:"groups"`
15 17
 }
16 18
 
17 19
 type UserList struct {
... ...
@@ -57,6 +57,28 @@ func ValidateIdentityName(name string, _ bool) (bool, string) {
57 57
 	return true, ""
58 58
 }
59 59
 
60
+func ValidateGroupName(name string, _ bool) (bool, string) {
61
+	if strings.Contains(name, "%") {
62
+		return false, `may not contain "%"`
63
+	}
64
+	if strings.Contains(name, "/") {
65
+		return false, `may not contain "/"`
66
+	}
67
+	if strings.Contains(name, ":") {
68
+		return false, `may not contain ":"`
69
+	}
70
+	if name == ".." {
71
+		return false, `may not equal ".."`
72
+	}
73
+	if name == "." {
74
+		return false, `may not equal "."`
75
+	}
76
+	if name == "~" {
77
+		return false, `may not equal "~"`
78
+	}
79
+	return true, ""
80
+}
81
+
60 82
 func ValidateIdentityProviderName(name string) (bool, string) {
61 83
 	if strings.Contains(name, "%") {
62 84
 		return false, `may not contain "%"`
... ...
@@ -83,6 +105,13 @@ func ValidateUser(user *api.User) fielderrors.ValidationErrorList {
83 83
 			allErrs = append(allErrs, fielderrors.NewFieldInvalid(fmt.Sprintf("identities[%d]", index), identity, msg))
84 84
 		}
85 85
 	}
86
+
87
+	for index, group := range user.Groups {
88
+		if ok, msg := ValidateGroupName(group, false); !ok {
89
+			allErrs = append(allErrs, fielderrors.NewFieldInvalid(fmt.Sprintf("groups[%d]", index), group, msg))
90
+		}
91
+	}
92
+
86 93
 	return allErrs
87 94
 }
88 95
 
... ...
@@ -93,7 +93,7 @@ func TestUserInitialization(t *testing.T) {
93 93
 	identityRegistry := identityregistry.NewRegistry(identityetcd.NewREST(etcdHelper))
94 94
 	useridentityMappingRegistry := useridentitymapping.NewRegistry(useridentitymapping.NewREST(userRegistry, identityRegistry))
95 95
 
96
-	lookup := identitymapper.NewLookupIdentityMapper(useridentityMappingRegistry)
96
+	lookup := identitymapper.NewLookupIdentityMapper(useridentityMappingRegistry, userRegistry)
97 97
 	provisioner := identitymapper.NewAlwaysCreateUserIdentityToUserMapper(identityRegistry, userRegistry)
98 98
 
99 99
 	testcases := map[string]struct {