// NewREST returns a RESTStorage object that will work against oauth clients func NewREST(s storage.Interface, clientGetter oauthclient.Getter) *REST { store := &etcdgeneric.Etcd{ NewFunc: func() runtime.Object { return &api.OAuthClientAuthorization{} }, NewListFunc: func() runtime.Object { return &api.OAuthClientAuthorizationList{} }, KeyRootFunc: func(ctx kapi.Context) string { return EtcdPrefix }, KeyFunc: func(ctx kapi.Context, name string) (string, error) { return util.NoNamespaceKeyFunc(ctx, EtcdPrefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.OAuthClientAuthorization).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return oauthclientauthorization.Matcher(label, field) }, QualifiedResource: api.Resource("oauthclientauthorizations"), Storage: s, } store.CreateStrategy = oauthclientauthorization.NewStrategy(clientGetter) store.UpdateStrategy = oauthclientauthorization.NewStrategy(clientGetter) return &REST{*store} }
// NewREST returns a RESTStorage object that will work against oauth clients func NewREST(optsGetter restoptions.Getter) (*REST, error) { store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.OAuthClient{} }, NewListFunc: func() runtime.Object { return &api.OAuthClientList{} }, KeyRootFunc: func(ctx kapi.Context) string { return EtcdPrefix }, KeyFunc: func(ctx kapi.Context, name string) (string, error) { return util.NoNamespaceKeyFunc(ctx, EtcdPrefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.OAuthClient).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return oauthclient.Matcher(label, field) }, QualifiedResource: api.Resource("oauthclients"), CreateStrategy: oauthclient.Strategy, UpdateStrategy: oauthclient.Strategy, } if err := restoptions.ApplyOptions(optsGetter, store, EtcdPrefix); err != nil { return nil, err } return &REST{*store}, nil }
// NewREST returns a RESTStorage object that will work against authorize tokens func NewREST(optsGetter restoptions.Getter, clientGetter oauthclient.Getter) (*REST, error) { strategy := oauthauthorizetoken.NewStrategy(clientGetter) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.OAuthAuthorizeToken{} }, NewListFunc: func() runtime.Object { return &api.OAuthAuthorizeTokenList{} }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.OAuthAuthorizeToken).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) *generic.SelectionPredicate { return oauthauthorizetoken.Matcher(label, field) }, TTLFunc: func(obj runtime.Object, existing uint64, update bool) (uint64, error) { token := obj.(*api.OAuthAuthorizeToken) expires := uint64(token.ExpiresIn) return expires, nil }, QualifiedResource: api.Resource("oauthauthorizetokens"), CreateStrategy: strategy, UpdateStrategy: strategy, } if err := restoptions.ApplyOptions(optsGetter, store, false, storage.NoTriggerPublisher); err != nil { return nil, err } return &REST{store}, nil }
// Get retrieves the OAuthClient from the indexer for a given namespace and name. func (s oAuthClientNamespaceLister) Get(name string) (*api.OAuthClient, error) { obj, exists, err := s.indexer.GetByKey(s.namespace + "/" + name) if err != nil { return nil, err } if !exists { return nil, errors.NewNotFound(api.Resource("oauthclient"), name) } return obj.(*api.OAuthClient), nil }
// NewREST returns a RESTStorage object that will work against access tokens func NewREST(optsGetter restoptions.Getter, clientGetter oauthclient.Getter, backends ...storage.Interface) (*REST, error) { strategy := oauthaccesstoken.NewStrategy(clientGetter) store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.OAuthAccessToken{} }, NewListFunc: func() runtime.Object { return &api.OAuthAccessTokenList{} }, KeyRootFunc: func(ctx kapi.Context) string { return EtcdPrefix }, KeyFunc: func(ctx kapi.Context, name string) (string, error) { return util.NoNamespaceKeyFunc(ctx, EtcdPrefix, name) }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.OAuthAccessToken).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) generic.Matcher { return oauthaccesstoken.Matcher(label, field) }, TTLFunc: func(obj runtime.Object, existing uint64, update bool) (uint64, error) { token := obj.(*api.OAuthAccessToken) expires := uint64(token.ExpiresIn) return expires, nil }, QualifiedResource: api.Resource("oauthaccesstokens"), CreateStrategy: strategy, UpdateStrategy: strategy, } if err := restoptions.ApplyOptions(optsGetter, store, EtcdPrefix); err != nil { return nil, err } if len(backends) > 0 { // Build identical stores that talk to a single etcd, so we can verify the token is distributed after creation watchers := []rest.Watcher{} for i := range backends { watcher := *store watcher.Storage = backends[i] watchers = append(watchers, &watcher) } // Observe the cluster for the particular resource version, requiring at least one backend to succeed observer := observe.NewClusterObserver(store.Storage.Versioner(), watchers, 1) // After creation, wait for the new token to propagate store.AfterCreate = func(obj runtime.Object) error { return observer.ObserveResourceVersion(obj.(*api.OAuthAccessToken).ResourceVersion, 5*time.Second) } } return &REST{store}, nil }
func TestAuthenticateTokenNotFound(t *testing.T) { tokenRegistry := &test.AccessTokenRegistry{Err: apierrs.NewNotFound(oapi.Resource("OAuthAccessToken"), "token")} userRegistry := usertest.NewUserRegistry() tokenAuthenticator := NewTokenAuthenticator(tokenRegistry, userRegistry, identitymapper.NoopGroupMapper{}) userInfo, found, err := tokenAuthenticator.AuthenticateToken("token") if found { t.Error("Found token, but it should be missing!") } if err == nil { t.Error("Expected not found error") } if !apierrs.IsNotFound(err) { t.Error("Expected not found error") } if userInfo != nil { t.Errorf("Unexpected user: %v", userInfo) } }
// NewREST returns a RESTStorage object that will work against oauth clients func NewREST(optsGetter restoptions.Getter) (*REST, error) { store := ®istry.Store{ NewFunc: func() runtime.Object { return &api.OAuthClient{} }, NewListFunc: func() runtime.Object { return &api.OAuthClientList{} }, ObjectNameFunc: func(obj runtime.Object) (string, error) { return obj.(*api.OAuthClient).Name, nil }, PredicateFunc: func(label labels.Selector, field fields.Selector) *generic.SelectionPredicate { return oauthclient.Matcher(label, field) }, QualifiedResource: api.Resource("oauthclients"), CreateStrategy: oauthclient.Strategy, UpdateStrategy: oauthclient.Strategy, } if err := restoptions.ApplyOptions(optsGetter, store, false, storage.NoTriggerPublisher); err != nil { return nil, err } return &REST{*store}, nil }
func TestClusterReaderCoverage(t *testing.T) { testutil.RequireEtcd(t) defer testutil.DumpEtcdOnFailure(t) _, clusterAdminKubeConfig, err := testserver.StartTestMasterAPI() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } discoveryClient := client.NewDiscoveryClient(clusterAdminClient.RESTClient) // (map[string]*unversioned.APIResourceList, error) allResourceList, err := discoveryClient.ServerResources() if err != nil { t.Fatalf("unexpected error: %v", err) } allResources := map[unversioned.GroupResource]bool{} for _, resources := range allResourceList { version, err := unversioned.ParseGroupVersion(resources.GroupVersion) if err != nil { t.Fatalf("unexpected error: %v", err) } for _, resource := range resources.APIResources { allResources[version.WithResource(resource.Name).GroupResource()] = true } } escalatingResources := map[unversioned.GroupResource]bool{ oauthapi.Resource("oauthauthorizetokens"): true, oauthapi.Resource("oauthaccesstokens"): true, oauthapi.Resource("oauthclients"): true, imageapi.Resource("imagestreams/secrets"): true, kapi.Resource("secrets"): true, kapi.Resource("pods/exec"): true, kapi.Resource("pods/proxy"): true, kapi.Resource("pods/portforward"): true, kapi.Resource("nodes/proxy"): true, kapi.Resource("services/proxy"): true, } readerRole, err := clusterAdminClient.ClusterRoles().Get(bootstrappolicy.ClusterReaderRoleName) if err != nil { t.Fatalf("unexpected error: %v", err) } for _, rule := range readerRole.Rules { for _, group := range rule.APIGroups { for resource := range rule.Resources { gr := unversioned.GroupResource{Group: group, Resource: resource} if escalatingResources[gr] { t.Errorf("cluster-reader role has escalating resource %v. Check pkg/cmd/server/bootstrappolicy/policy.go.", gr) } delete(allResources, gr) } } } // remove escalating resources that cluster-reader should not have access to for resource := range escalatingResources { delete(allResources, resource) } // remove resources without read APIs nonreadingResources := []unversioned.GroupResource{ buildapi.Resource("buildconfigs/instantiatebinary"), buildapi.Resource("buildconfigs/instantiate"), buildapi.Resource("builds/clone"), deployapi.Resource("deploymentconfigrollbacks"), deployapi.Resource("generatedeploymentconfigs"), deployapi.Resource("deploymentconfigs/rollback"), imageapi.Resource("imagestreamimports"), imageapi.Resource("imagestreammappings"), extensionsapi.Resource("deployments/rollback"), kapi.Resource("pods/attach"), kapi.Resource("namespaces/finalize"), } for _, resource := range nonreadingResources { delete(allResources, resource) } // anything left in the map is missing from the permissions if len(allResources) > 0 { t.Errorf("cluster-reader role is missing %v. Check pkg/cmd/server/bootstrappolicy/policy.go.", allResources) } }
func TestRegistryAndServer(t *testing.T) { ch := make(chan *http.Request, 1) assertServer := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { ch <- req })) validClient := &oapi.OAuthClient{ ObjectMeta: kapi.ObjectMeta{Name: "test"}, Secret: "secret", RedirectURIs: []string{assertServer.URL + "/assert"}, } restrictedClient := &oapi.OAuthClient{ ObjectMeta: kapi.ObjectMeta{Name: "test"}, Secret: "secret", RedirectURIs: []string{assertServer.URL + "/assert"}, ScopeRestrictions: []oapi.ScopeRestriction{ {ExactValues: []string{"user:info"}}, }, } testCases := map[string]struct { Client *oapi.OAuthClient ClientAuth *oapi.OAuthClientAuthorization AuthSuccess bool AuthUser user.Info Scope string Check func(*testHandlers, *http.Request) }{ "needs auth": { Client: validClient, Check: func(h *testHandlers, _ *http.Request) { if !h.AuthNeed || h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil || h.HandleAuthorizeReq.Authorized { t.Errorf("expected request to need authentication: %#v", h) } }, }, "needs grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, Check: func(h *testHandlers, _ *http.Request) { if h.AuthNeed || !h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil || h.HandleAuthorizeReq.Authorized { t.Errorf("expected request to need to grant access: %#v", h) } }, }, "invalid scope": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, Scope: "some-scope", Check: func(h *testHandlers, _ *http.Request) { if h.AuthNeed || h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil || h.HandleAuthorizeReq.Authorized || h.HandleAuthorizeResp.ErrorId != "invalid_scope" { t.Errorf("expected invalid_scope error: %#v, %#v, %#v", h, h.HandleAuthorizeReq, h.HandleAuthorizeResp) } }, }, "disallowed scope": { Client: restrictedClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, Scope: "user:full", Check: func(h *testHandlers, _ *http.Request) { if h.AuthNeed || h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil || h.HandleAuthorizeReq.Authorized || h.HandleAuthorizeResp.ErrorId != "access_denied" { t.Errorf("expected access_denied error: %#v, %#v, %#v", h, h.HandleAuthorizeReq, h.HandleAuthorizeResp) } }, }, "has non covered grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, ClientAuth: &oapi.OAuthClientAuthorization{ UserName: "******", ClientName: "test", Scopes: []string{"user:info"}, }, Scope: "user:info user:check-access", Check: func(h *testHandlers, req *http.Request) { if h.AuthNeed || !h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil || h.HandleAuthorizeReq.Authorized { t.Errorf("expected request to need to grant access because of uncovered scopes: %#v", h) } }, }, "has covered grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, ClientAuth: &oapi.OAuthClientAuthorization{ UserName: "******", ClientName: "test", Scopes: []string{"user:info", "user:check-access"}, }, Scope: "user:info user:check-access", Check: func(h *testHandlers, req *http.Request) { if h.AuthNeed || h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil || !h.HandleAuthorizeReq.Authorized || h.HandleAuthorizeResp.ErrorId != "" { t.Errorf("unexpected flow: %#v, %#v, %#v", h, h.HandleAuthorizeReq, h.HandleAuthorizeResp) } }, }, "has auth and grant": { Client: validClient, AuthSuccess: true, AuthUser: &user.DefaultInfo{ Name: "user", }, ClientAuth: &oapi.OAuthClientAuthorization{ UserName: "******", ClientName: "test", Scopes: []string{"user:full"}, }, Check: func(h *testHandlers, req *http.Request) { if h.AuthNeed || h.GrantNeed || h.AuthErr != nil || h.GrantErr != nil || !h.HandleAuthorizeReq.Authorized || h.HandleAuthorizeResp.ErrorId != "" { t.Errorf("unexpected flow: %#v, %#v, %#v", h, h.HandleAuthorizeReq, h.HandleAuthorizeResp) return } if req == nil { t.Errorf("unexpected nil assertion request") return } if code := req.URL.Query().Get("code"); code == "" { t.Errorf("expected query param 'code', got: %#v", req) } }, }, } for _, testCase := range testCases { h := &testHandlers{} h.Authenticate = testCase.AuthSuccess h.User = testCase.AuthUser access, authorize := &test.AccessTokenRegistry{}, &test.AuthorizeTokenRegistry{} client := &test.ClientRegistry{ Client: testCase.Client, } if testCase.Client == nil { client.Err = apierrs.NewNotFound(oapi.Resource("OAuthClient"), "unknown") } grant := &test.ClientAuthorizationRegistry{ ClientAuthorization: testCase.ClientAuth, } if testCase.ClientAuth == nil { grant.GetErr = apierrs.NewNotFound(oapi.Resource("OAuthClientAuthorization"), "test:test") } storage := registrystorage.New(access, authorize, client, NewUserConversion()) config := osinserver.NewDefaultServerConfig() h.AuthorizeHandler = osinserver.AuthorizeHandlers{ handlers.NewAuthorizeAuthenticator( h, h, h, ), handlers.NewGrantCheck( NewClientAuthorizationGrantChecker(grant), h, h, ), } server := osinserver.New( config, storage, h, osinserver.AccessHandlers{ handlers.NewDenyAccessAuthenticator(), }, h, ) mux := http.NewServeMux() server.Install(mux, "") s := httptest.NewServer(mux) oaclientConfig := &osincli.ClientConfig{ ClientId: "test", ClientSecret: "secret", RedirectUrl: assertServer.URL + "/assert", AuthorizeUrl: s.URL + "/authorize", TokenUrl: s.URL + "/token", Scope: testCase.Scope, } oaclient, err := osincli.NewClient(oaclientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } aReq := oaclient.NewAuthorizeRequest(osincli.CODE) if _, err := http.Get(aReq.GetAuthorizeUrl().String()); err != nil { t.Fatalf("unexpected error: %v", err) } var req *http.Request select { case out := <-ch: req = out default: } testCase.Check(h, req) } }
func emptyAuthRegistry() *test.ClientAuthorizationRegistry { return &test.ClientAuthorizationRegistry{ GetErr: kapierrors.NewNotFound(oapi.Resource("oauthclientauthorizations"), "foo"), } }