| ... | ... |
@@ -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 |
| ... | ... |
@@ -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 {
|