// Clients returns an OpenShift and Kubernetes client with the credentials of the named service account // TODO: change return types to client.Interface/kclient.Interface to allow auto-reloading credentials func Clients(config kclient.Config, tokenRetriever TokenRetriever, namespace, name string) (*client.Client, *kclient.Client, error) { // Clear existing auth info config.Username = "" config.Password = "" config.CertFile = "" config.CertData = []byte{} config.KeyFile = "" config.KeyData = []byte{} // For now, just initialize the token once // TODO: refetch the token if the client encounters 401 errors token, err := tokenRetriever.GetToken(namespace, name) if err != nil { return nil, nil, err } config.BearerToken = token c, err := client.New(&config) if err != nil { return nil, nil, err } kc, err := kclient.New(&config) if err != nil { return nil, nil, err } return c, kc, nil }
func TestUnprivilegedNewProject(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // confirm that we have access to request the project allowed, err := valerieOpenshiftClient.ProjectRequests().List(labels.Everything(), fields.Everything()) if err != nil { t.Fatalf("unexpected error: %v", err) } if allowed.Status != kapi.StatusSuccess { t.Fatalf("expected %v, got %v", kapi.StatusSuccess, allowed.Status) } requestProject := oc.NewProjectOptions{ ProjectName: "new-project", DisplayName: "display name here", Description: "the special description", Client: valerieOpenshiftClient, Out: ioutil.Discard, } if err := requestProject.Run(); err != nil { t.Fatalf("unexpected error: %v", err) } waitForProject(t, valerieOpenshiftClient, "new-project", 5*time.Second, 10) if err := requestProject.Run(); !kapierrors.IsAlreadyExists(err) { t.Fatalf("expected an already exists error, but got %v", err) } }
func validateToken(token string, clientConfig *kclient.Config) { if len(token) == 0 { fmt.Println("You must provide a token to validate") return } fmt.Printf("Using token: %v\n", token) clientConfig.BearerToken = token osClient, err := osclient.New(clientConfig) if err != nil { fmt.Printf("Error building osClient: %v\n", err) return } jsonResponse, _, err := getTokenInfo(token, osClient) if err != nil { fmt.Printf("%v\n", err) fmt.Println("Try visiting " + getRequestTokenURL(clientConfig) + " for a new token.") return } fmt.Printf("%v\n", string(jsonResponse)) whoami, err := osClient.Users().Get("~") if err != nil { fmt.Printf("Error making whoami request: %v\n", err) return } whoamiJSON, err := json.Marshal(whoami) if err != nil { fmt.Printf("Error interpretting whoami response: %v\n", err) return } fmt.Printf("%v\n", string(whoamiJSON)) }
func (o LogoutOptions) RunLogout() error { token := o.Config.BearerToken client, err := client.New(o.Config) if err != nil { return err } userInfo, err := whoAmI(client) if err != nil { return err } if err := client.OAuthAccessTokens().Delete(token); err != nil { return err } newConfig := *o.StartingKubeConfig for key, value := range newConfig.AuthInfos { if value.Token == token { value.Token = "" newConfig.AuthInfos[key] = value // don't break, its possible that more than one user stanza has the same token. } } if err := kcmdconfig.ModifyConfig(o.PathOptions, newConfig); err != nil { return err } fmt.Fprintf(o.Out, "Logged %q out on %q\n", userInfo.Name, o.Config.Host) return nil }
func (o LoginOptions) whoAmI() (*api.User, error) { client, err := client.New(o.Config) if err != nil { return nil, err } return whoAmI(client) }
func TestBootstrapPolicyAuthenticatedUsersAgainstOpenshiftNamespace(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } openshiftSharedResourcesNamespace := "openshift" if _, err := valerieOpenshiftClient.Templates(openshiftSharedResourcesNamespace).List(labels.Everything(), fields.Everything()); err != nil { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.Templates(kapi.NamespaceDefault).List(labels.Everything(), fields.Everything()); err == nil || !kapierror.IsForbidden(err) { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreams(openshiftSharedResourcesNamespace).List(labels.Everything(), fields.Everything()); err != nil { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreams(kapi.NamespaceDefault).List(labels.Everything(), fields.Everything()); err == nil || !kapierror.IsForbidden(err) { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreamTags(openshiftSharedResourcesNamespace).Get("name", "tag"); !kapierror.IsNotFound(err) { t.Errorf("unexpected error: %v", err) } if _, err := valerieOpenshiftClient.ImageStreamTags(kapi.NamespaceDefault).Get("name", "tag"); err == nil || !kapierror.IsForbidden(err) { t.Errorf("unexpected error: %v", err) } }
func GetClusterAdminClient(adminKubeConfigFile string) (*client.Client, error) { clientConfig, err := GetClusterAdminClientConfig(adminKubeConfigFile) if err != nil { return nil, err } osClient, err := client.New(clientConfig) if err != nil { return nil, err } return osClient, nil }
func TestUnprivilegedNewProjectDenied(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } role, err := clusterAdminClient.ClusterRoles().Get(bootstrappolicy.SelfProvisionerRoleName) if err != nil { t.Fatalf("unexpected error: %v", err) } role.Rules = []authorizationapi.PolicyRule{} if _, err := clusterAdminClient.ClusterRoles().Update(role); err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // confirm that we have access to request the project _, err = valerieOpenshiftClient.ProjectRequests().List(labels.Everything(), fields.Everything()) if err == nil { t.Fatalf("expected error: %v", err) } expectedError := `You may not request a new project via this API.` if (err != nil) && (err.Error() != expectedError) { t.Fatalf("expected\n\t%v\ngot\n\t%v", expectedError, err.Error()) } }
func NewUserOpenShiftClient(bearerToken string) (*osclient.Client, error) { config, err := openShiftClientConfig() if err != nil { return nil, err } config.BearerToken = bearerToken client, err := osclient.New(config) if err != nil { return nil, fmt.Errorf("error creating OpenShift client: %s", err) } return client, nil }
func TestBootstrapPolicySelfSubjectAccessReviews(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // can I get a subjectaccessreview on myself even if I have no rights to do it generally askCanICreatePolicyBindings := &authorizationapi.SubjectAccessReview{Verb: "create", Resource: "policybindings"} subjectAccessReviewTest{ clientInterface: valerieOpenshiftClient.SubjectAccessReviews("openshift"), review: askCanICreatePolicyBindings, response: authorizationapi.SubjectAccessReviewResponse{ Allowed: false, Reason: `User "valerie" cannot create policybindings in project "openshift"`, Namespace: "openshift", }, }.run(t) // I shouldn't be allowed to ask whether someone else can perform an action askCanClusterAdminsCreateProject := &authorizationapi.SubjectAccessReview{Groups: util.NewStringSet("system:cluster-admins"), Verb: "create", Resource: "projects"} subjectAccessReviewTest{ clientInterface: valerieOpenshiftClient.SubjectAccessReviews("openshift"), review: askCanClusterAdminsCreateProject, err: `User "valerie" cannot create subjectaccessreviews in project "openshift"`, }.run(t) }
func whoami(clientCfg *kclient.Config) (*api.User, error) { oClient, err := client.New(clientCfg) if err != nil { return nil, err } me, err := oClient.Users().Get("~") if err != nil { return nil, err } return me, nil }
// Clients returns an OpenShift and a Kubernetes client from a given configuration func (cfg *Config) Clients() (osclient.Interface, kclient.Interface, error) { cfg.bindEnv() kubeClient, err := kclient.New(cfg.KubeConfig()) if err != nil { return nil, nil, fmt.Errorf("Unable to configure Kubernetes client: %v", err) } osClient, err := osclient.New(cfg.OpenShiftConfig()) if err != nil { return nil, nil, fmt.Errorf("Unable to configure OpenShift client: %v", err) } return osClient, kubeClient, nil }
func GetClientForUser(clientConfig kclient.Config, username string) (*client.Client, error) { token, err := tokencmd.RequestToken(&clientConfig, nil, username, "password") if err != nil { return nil, err } userClientConfig := clientConfig userClientConfig.BearerToken = token userClientConfig.Username = "" userClientConfig.Password = "" userClientConfig.TLSClientConfig.CertFile = "" userClientConfig.TLSClientConfig.KeyFile = "" userClientConfig.TLSClientConfig.CertData = nil userClientConfig.TLSClientConfig.KeyData = nil return client.New(&userClientConfig) }
// GetContextNicknameFromConfig returns "namespace/GetClusterNicknameFromConfig/username(as known by the server)". This allows tab completion for switching projects/context // to work easily. First tab is the most selective on project. Second stanza in the next most selective on cluster name. The chances of a user trying having // one projects on a single server that they want to operate against with two identities is low, so username is last. func GetContextNicknameFromConfig(namespace string, clientCfg *client.Config) (string, error) { client, err := osclient.New(clientCfg) if err != nil { return "", err } userInfo, err := client.Users().Get("~") if err != nil { return "", err } clusterNick, err := GetClusterNicknameFromConfig(clientCfg) if err != nil { return "", err } return namespace + "/" + clusterNick + "/" + userInfo.Name, nil }
// ClientForVersion initializes or reuses a client for the specified version, or returns an // error if that is not possible func (c *clientCache) ClientForVersion(version string) (*client.Client, error) { config, err := c.ClientConfigForVersion(version) if err != nil { return nil, err } if client, ok := c.clients[config.Version]; ok { return client, nil } client, err := client.New(config) if err != nil { return nil, err } c.clients[config.Version] = client return client, nil }
// RequestToken uses the cmd arguments to locate an openshift oauth server and attempts to authenticate // it returns the access token if it gets one. An error if it does not func RequestToken(clientCfg *kclient.Config, reader io.Reader, defaultUsername string, defaultPassword string) (string, error) { tokenGetter := &tokenGetterInfo{} osClient, err := client.New(clientCfg) if err != nil { return "", err } // get the transport, so that we can use it to build our own client that wraps it // our client understands certain challenges and can respond to them clientTransport, err := kclient.TransportFor(clientCfg) if err != nil { return "", err } httpClient := &http.Client{ Transport: clientTransport, CheckRedirect: tokenGetter.checkRedirect, } osClient.Client = &challengingClient{httpClient, reader, defaultUsername, defaultPassword} result := osClient.Get().AbsPath(server.OpenShiftOAuthAPIPrefix, osinserver.AuthorizePath). Param("response_type", "token"). Param("client_id", "openshift-challenging-client"). Do() if err := result.Error(); err != nil && !isRedirectError(err) { return "", err } if len(tokenGetter.accessToken) == 0 { r, _ := result.Raw() if description, ok := rawOAuthJSONErrorDescription(r); ok { return "", fmt.Errorf("cannot retrieve a token: %s", description) } glog.V(4).Infof("A request token could not be created, server returned: %s", string(r)) return "", fmt.Errorf("the server did not return a token (possible server error)") } return tokenGetter.accessToken, nil }
func NewAutoLinkBuildsFromEnvironment() (*AutoLinkBuilds, error) { config := &AutoLinkBuilds{} file := os.Getenv("AUTOLINK_CONFIG") if len(file) == 0 { return nil, ErrNotEnabled } clientConfig, namespace, err := clientFromConfig(file) if err != nil { return nil, err } client, err := client.New(clientConfig) if err != nil { return nil, err } config.Client = client if value := os.Getenv("AUTOLINK_NAMESPACE"); len(value) > 0 { namespace = value } if len(namespace) == 0 { return nil, ErrNotEnabled } if value := os.Getenv("AUTOLINK_HOOK"); len(value) > 0 { abs, err := filepath.Abs(value) if err != nil { return nil, err } if _, err := os.Stat(abs); err != nil { return nil, err } config.PostReceiveHook = abs } config.Namespaces = []string{namespace} config.CurrentNamespace = namespace return config, nil }
// TODO: clients should be copied and instantiated from a common client config, tweaked, then // given to individual controllers and other infrastructure components. func GetOpenShiftClient(kubeConfigFile string) (*client.Client, *kclient.Config, error) { loadingRules := &clientcmd.ClientConfigLoadingRules{} loadingRules.ExplicitPath = kubeConfigFile loader := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, &clientcmd.ConfigOverrides{}) kubeConfig, err := loader.ClientConfig() if err != nil { return nil, nil, err } // This is an internal client which is shared by most controllers, so boost default QPS // TODO: this should be configured by the caller, not in this method. kubeConfig.QPS = 150.0 kubeConfig.Burst = 300 kubeConfig.WrapTransport = DefaultClientTransport openshiftClient, err := client.New(kubeConfig) if err != nil { return nil, nil, err } return openshiftClient, kubeConfig, nil }
func NewRegistryOpenShiftClient() (*osclient.Client, error) { config, err := openShiftClientConfig() if err != nil { return nil, err } if !config.Insecure { certData := os.Getenv("OPENSHIFT_CERT_DATA") if len(certData) == 0 { return nil, errors.New("OPENSHIFT_CERT_DATA is required") } certKeyData := os.Getenv("OPENSHIFT_KEY_DATA") if len(certKeyData) == 0 { return nil, errors.New("OPENSHIFT_KEY_DATA is required") } config.TLSClientConfig.CertData = []byte(certData) config.TLSClientConfig.KeyData = []byte(certKeyData) } client, err := osclient.New(config) if err != nil { return nil, fmt.Errorf("error creating OpenShift client: %s", err) } return client, nil }
func (a *Authenticator) AuthenticatePassword(username, password string) (user.Info, bool, error) { token, ok, err := a.token.AuthenticatePassword(username, password) if !ok || err != nil { return nil, false, err } auth := oclient.OAuthWrapper{a.rt, token} client, err := client.New(&kclient.Config{Transport: auth, Host: a.host}) if err != nil { return nil, false, err } u, err := client.Users().Get("~") if err != nil { return nil, false, err } info := &user.DefaultInfo{ Name: u.Name, UID: string(u.UID), } return info, true, nil }
func TestUnprivilegedNewProjectFromTemplate(t *testing.T) { namespace := "foo" templateName := "bar" masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.ProjectConfig.ProjectRequestTemplate = namespace + "/" + templateName clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } if _, err := clusterAdminClient.Projects().Create(&projectapi.Project{ObjectMeta: kapi.ObjectMeta{Name: namespace}}); err != nil { t.Fatalf("unexpected error: %v", err) } template := projectrequeststorage.DefaultTemplate() template.Name = templateName template.Namespace = namespace template.Objects[0].(*projectapi.Project).Annotations["extra"] = "here" _, err = clusterAdminClient.Templates(namespace).Create(template) if err != nil { t.Fatalf("unexpected error: %v", err) } requestProject := oc.NewProjectOptions{ ProjectName: "new-project", DisplayName: "display name here", Description: "the special description", Client: valerieOpenshiftClient, Out: ioutil.Discard, } if err := requestProject.Run(); err != nil { t.Fatalf("unexpected error: %v", err) } waitForProject(t, valerieOpenshiftClient, "new-project", 5*time.Second, 10) project, err := valerieOpenshiftClient.Projects().Get("new-project") if err != nil { t.Fatalf("unexpected error: %v", err) } if project.Annotations["extra"] != "here" { t.Errorf("unexpected project %#v", project) } if err := clusterAdminClient.Templates(namespace).Delete(templateName); err != nil { t.Fatalf("unexpected error: %v", err) } requestProject.ProjectName = "different" // This should fail during the template retrieve if err := requestProject.Run(); !kapierrors.IsNotFound(err) { t.Fatalf("expected a not found error, but got %v", err) } }
// getClientConfig returns back the current clientConfig as we know it. If there is no clientConfig, it builds one with enough information // to talk to a server. This may involve user prompts. This method is not threadsafe. func (o *LoginOptions) getClientConfig() (*kclient.Config, error) { if o.Config != nil { return o.Config, nil } clientConfig := &kclient.Config{} if len(o.Server) == 0 { // we need to have a server to talk to if cmdutil.IsTerminal(o.Reader) { for !o.serverProvided() { defaultServer := defaultClusterURL promptMsg := fmt.Sprintf("Atomic Enterprise server [%s]: ", defaultServer) o.Server = cmdutil.PromptForStringWithDefault(o.Reader, defaultServer, promptMsg) } } } // normalize the provided server to a format expected by config serverNormalized, err := config.NormalizeServerURL(o.Server) if err != nil { return nil, err } o.Server = serverNormalized clientConfig.Host = o.Server if len(o.CAFile) > 0 { clientConfig.CAFile = o.CAFile } else { // check all cluster stanzas to see if we already have one with this URL that contains a client cert for _, cluster := range o.StartingKubeConfig.Clusters { if cluster.Server == clientConfig.Host { if len(cluster.CertificateAuthority) > 0 { clientConfig.CAFile = cluster.CertificateAuthority break } if len(cluster.CertificateAuthorityData) > 0 { clientConfig.CAData = cluster.CertificateAuthorityData break } } } } // ping to check if server is reachable osClient, err := client.New(clientConfig) if err != nil { return nil, err } result := osClient.Get().AbsPath("/osapi").Do() if result.Error() != nil { switch { case o.InsecureTLS: clientConfig.Insecure = true // certificate issue, prompt user for insecure connection case clientcmd.IsCertificateAuthorityUnknown(result.Error()): // check to see if we already have a cluster stanza that tells us to use --insecure for this particular server. If we don't, then prompt clientConfigToTest := *clientConfig clientConfigToTest.Insecure = true matchingClusters := getMatchingClusters(clientConfigToTest, *o.StartingKubeConfig) if len(matchingClusters) > 0 { clientConfig.Insecure = true } else if cmdutil.IsTerminal(o.Reader) { fmt.Fprintln(o.Out, "The server uses a certificate signed by an unknown authority.") fmt.Fprintln(o.Out, "You can bypass the certificate check, but any data you send to the server could be intercepted by others.") clientConfig.Insecure = cmdutil.PromptForBool(os.Stdin, "Use insecure connections? (y/n): ") if !clientConfig.Insecure { return nil, fmt.Errorf(clientcmd.GetPrettyMessageFor(result.Error())) } fmt.Fprintln(o.Out) } default: return nil, result.Error() } } // check for matching api version if len(o.APIVersion) > 0 { clientConfig.Version = o.APIVersion } o.Config = clientConfig return o.Config, nil }
func TestTemplate(t *testing.T) { _, path, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } for _, version := range []string{"v1", "v1beta3"} { config, err := testutil.GetClusterAdminClientConfig(path) if err != nil { t.Fatalf("unexpected error: %v", err) } config.Version = version config.Prefix = "" c, err := client.New(config) if err != nil { t.Fatalf("unexpected error: %v", err) } template := &templateapi.Template{ Parameters: []templateapi.Parameter{ { Name: "NAME", Value: "test", }, }, Objects: []runtime.Object{ &v1beta3.Service{ ObjectMeta: v1beta3.ObjectMeta{ Name: "${NAME}-tester", Namespace: "somevalue", }, Spec: v1beta3.ServiceSpec{ PortalIP: "1.2.3.4", SessionAffinity: "some-bad-${VALUE}", }, }, }, } obj, err := c.TemplateConfigs("default").Create(template) if err != nil { t.Fatalf("unexpected error: %v", err) } if len(obj.Objects) != 1 { t.Fatalf("unexpected object: %#v", obj) } if err := runtime.DecodeList(obj.Objects, runtime.UnstructuredJSONScheme); err != nil { t.Fatalf("unexpected error: %v", err) } svc := obj.Objects[0].(*runtime.Unstructured).Object spec := svc["spec"].(map[string]interface{}) meta := svc["metadata"].(map[string]interface{}) // keep existing values if spec["portalIP"] != "1.2.3.4" { t.Fatalf("unexpected object: %#v", svc) } // replace a value if meta["name"] != "test-tester" { t.Fatalf("unexpected object: %#v", svc) } // clear namespace if meta["namespace"] != "" { t.Fatalf("unexpected object: %#v", svc) } // preserve values exactly if spec["sessionAffinity"] != "some-bad-${VALUE}" { t.Fatalf("unexpected object: %#v", svc) } } }
// Discover the projects available for the stabilished session and take one to use. It // fails in case of no existing projects, and print out useful information in case of // multiple projects. // Requires o.Username to be set. func (o *LoginOptions) gatherProjectInfo() error { me, err := o.whoAmI() if err != nil { return err } if o.Username != me.Name { return fmt.Errorf("current user, %v, does not match expected user %v", me.Name, o.Username) } oClient, err := client.New(o.Config) if err != nil { return err } projects, err := oClient.Projects().List(labels.Everything(), fields.Everything()) if err != nil { return err } projectsItems := projects.Items switch len(projectsItems) { case 0: fmt.Fprintf(o.Out, `You don't have any projects. You can try to create a new project, by running $ oc new-project <projectname> `) o.Project = o.DefaultNamespace case 1: o.Project = projectsItems[0].Name fmt.Fprintf(o.Out, "Using project %q.\n", o.Project) default: projects := util.StringSet{} for _, project := range projectsItems { projects.Insert(project.Name) } namespace := o.DefaultNamespace if !projects.Has(namespace) { if namespace != kapi.NamespaceDefault && projects.Has(kapi.NamespaceDefault) { namespace = kapi.NamespaceDefault } else { namespace = projects.List()[0] } } if current, err := oClient.Projects().Get(namespace); err == nil { o.Project = current.Name fmt.Fprintf(o.Out, "Using project %q.\n", o.Project) } else if !kerrors.IsNotFound(err) && !clientcmd.IsForbidden(err) { return err } fmt.Fprintf(o.Out, "\nYou have access to the following projects and can switch between them with 'oc project <projectname>':\n\n") for _, p := range projects.List() { if o.Project == p { fmt.Fprintf(o.Out, " * %s (current)\n", p) } else { fmt.Fprintf(o.Out, " * %s\n", p) } } fmt.Fprintln(o.Out) } return nil }
// Negotiate a bearer token with the auth server, or try to reuse one based on the // information already present. In case of any missing information, ask for user input // (usually username and password, interactive depending on the Reader). func (o *LoginOptions) gatherAuthInfo() error { directClientConfig, err := o.getClientConfig() if err != nil { return err } // make a copy and use it to avoid mutating the original t := *directClientConfig clientConfig := &t // if a token were explicitly provided, try to use it if o.tokenProvided() { clientConfig.BearerToken = o.Token if osClient, err := client.New(clientConfig); err == nil { me, err := whoAmI(osClient) if err == nil { o.Username = me.Name o.Config = clientConfig fmt.Fprintf(o.Out, "Logged into %q as %q using the token provided.\n\n", o.Config.Host, o.Username) return nil } if !kerrors.IsUnauthorized(err) { return err } fmt.Fprintln(o.Out, "The token provided is invalid (probably expired).\n") } } // if a token was provided try to make use of it // make sure we have a username before continuing if !o.usernameProvided() { if cmdutil.IsTerminal(o.Reader) { for !o.usernameProvided() { o.Username = cmdutil.PromptForString(o.Reader, "Username: "******"Already logged into %q as %q.\n\n", o.Config.Host, o.Username) } return nil } } } } } // if kubeconfig doesn't already have a matching user stanza... clientConfig.BearerToken = "" clientConfig.CertData = []byte{} clientConfig.KeyData = []byte{} clientConfig.CertFile = o.CertFile clientConfig.KeyFile = o.KeyFile token, err := tokencmd.RequestToken(o.Config, o.Reader, o.Username, o.Password) if err != nil { return err } clientConfig.BearerToken = token osClient, err := client.New(clientConfig) if err != nil { return err } me, err := whoAmI(osClient) if err != nil { return err } o.Username = me.Name o.Config = clientConfig fmt.Fprintln(o.Out, "Login successful.\n") return nil }
func TestOAuthBasicAuthPassword(t *testing.T) { remotePrefix := "remote" expectedLogin := "******" expectedPassword := "******" expectedAuthHeader := "Basic " + base64.StdEncoding.EncodeToString([]byte(expectedLogin+":"+expectedPassword)) expectedUsername := remotePrefix + expectedLogin // Create tempfiles with certs and keys we're going to use certNames := map[string]string{} for certName, certContents := range basicAuthCerts { f, err := ioutil.TempFile("", certName) if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(f.Name()) if err := ioutil.WriteFile(f.Name(), certContents, os.FileMode(0600)); err != nil { t.Fatalf("unexpected error: %v", err) } certNames[certName] = f.Name() } // Build client cert pool clientCAs, err := util.CertPoolFromFile(certNames[basicAuthRemoteCACert]) if err != nil { t.Fatalf("unexpected error: %v", err) } // Build remote handler remoteHandler := http.HandlerFunc(func(w http.ResponseWriter, req *http.Request) { if req.TLS == nil { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Expected TLS") } if len(req.TLS.VerifiedChains) != 1 { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Expected peer cert verified by server") } if req.Header.Get("Authorization") != expectedAuthHeader { w.WriteHeader(http.StatusUnauthorized) t.Fatalf("Unexpected auth header: %s", req.Header.Get("Authorization")) } w.Header().Set("Content-Type", "application/json") w.Write([]byte(fmt.Sprintf(`{"sub":"%s"}`, expectedUsername))) }) // Start remote server remoteAddr, err := testutil.FindAvailableBindAddress(9443, 9999) if err != nil { t.Fatalf("Couldn't get free address for test server: %v", err) } remoteServer := &http.Server{ Addr: remoteAddr, Handler: remoteHandler, ReadTimeout: 10 * time.Second, WriteTimeout: 10 * time.Second, MaxHeaderBytes: 1 << 20, TLSConfig: &tls.Config{ // Change default from SSLv3 to TLSv1.0 (because of POODLE vulnerability) MinVersion: tls.VersionTLS10, // RequireAndVerifyClientCert lets us limit requests to ones with a valid client certificate ClientAuth: tls.RequireAndVerifyClientCert, ClientCAs: clientCAs, }, } go func() { if err := remoteServer.ListenAndServeTLS(certNames[basicAuthRemoteServerCert], certNames[basicAuthRemoteServerKey]); err != nil { t.Fatalf("unexpected error: %v", err) } }() // Build master config masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: "basicauth", UseAsChallenger: true, UseAsLogin: true, Provider: runtime.EmbeddedObject{ &configapi.BasicAuthPasswordIdentityProvider{ RemoteConnectionInfo: configapi.RemoteConnectionInfo{ URL: fmt.Sprintf("https://%s", remoteAddr), CA: certNames[basicAuthRemoteCACert], ClientCert: configapi.CertInfo{ CertFile: certNames[basicAuthClientCert], KeyFile: certNames[basicAuthClientKey], }, }, }, }, } // Start server clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Use the server and CA info anonConfig := kclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Make sure we can get a token accessToken, err := tokencmd.RequestToken(&anonConfig, nil, expectedLogin, expectedPassword) if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(accessToken) == 0 { t.Errorf("Expected access token, got none") } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("Unexpected error: %v", err) } if user.Name != expectedUsername { t.Fatalf("Expected username as the user, got %v", user) } }
func TestBasicGroupManipulation(t *testing.T) { _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig := *clusterAdminClientConfig valerieClientConfig.Username = "" valerieClientConfig.Password = "" valerieClientConfig.BearerToken = "" valerieClientConfig.CertFile = "" valerieClientConfig.KeyFile = "" valerieClientConfig.CertData = nil valerieClientConfig.KeyData = nil accessToken, err := tokencmd.RequestToken(&valerieClientConfig, nil, "valerie", "security!") if err != nil { t.Fatalf("unexpected error: %v", err) } valerieClientConfig.BearerToken = accessToken valerieOpenshiftClient, err := client.New(&valerieClientConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // make sure we don't get back system groups firstValerie, err := clusterAdminClient.Users().Get("valerie") if err != nil { t.Fatalf("unexpected error: %v", err) } if len(firstValerie.Groups) != 0 { t.Errorf("unexpected groups: %v", firstValerie.Groups) } // make sure that user/~ returns groups for unbacked users expectedClusterAdminGroups := []string{"system:cluster-admins"} clusterAdminUser, err := clusterAdminClient.Users().Get("~") if err != nil { t.Fatalf("unexpected error: %v", err) } if !reflect.DeepEqual(clusterAdminUser.Groups, expectedClusterAdminGroups) { t.Errorf("expected %v, got %v", clusterAdminUser.Groups, expectedClusterAdminGroups) } valerieGroups := []string{"thegroup"} firstValerie.Groups = append(firstValerie.Groups, valerieGroups...) _, err = clusterAdminClient.Users().Update(firstValerie) if err != nil { t.Errorf("unexpected error: %v", err) } // make sure that user/~ doesn't get back system groups when it merges secondValerie, err := valerieOpenshiftClient.Users().Get("~") if err != nil { t.Fatalf("unexpected error: %v", err) } if !reflect.DeepEqual(secondValerie.Groups, valerieGroups) { t.Errorf("expected %v, got %v", secondValerie.Groups, valerieGroups) } _, err = valerieOpenshiftClient.Projects().Get("empty") if err == nil { t.Fatalf("expected error") } emptyProject := &projectapi.Project{} emptyProject.Name = "empty" _, err = clusterAdminClient.Projects().Create(emptyProject) if err != nil { t.Fatalf("unexpected error: %v", err) } roleBinding := &authorizationapi.RoleBinding{} roleBinding.Name = "admins" roleBinding.RoleRef.Name = "admin" roleBinding.Groups = util.NewStringSet(valerieGroups...) _, err = clusterAdminClient.RoleBindings("empty").Create(roleBinding) if err != nil { t.Fatalf("unexpected error: %v", err) } // make sure that user groups are respected for policy _, err = valerieOpenshiftClient.Projects().Get("empty") if err != nil { t.Fatalf("unexpected error: %v", err) } }
func TestOAuthRequestHeader(t *testing.T) { // Write cert we're going to use to verify OAuth requestheader requests caFile, err := ioutil.TempFile("", "test.crt") if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(caFile.Name()) if err := ioutil.WriteFile(caFile.Name(), rootCACert, os.FileMode(0600)); err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: "requestheader", UseAsChallenger: false, UseAsLogin: false, Provider: runtime.EmbeddedObject{ &configapi.RequestHeaderIdentityProvider{ ClientCA: caFile.Name(), Headers: []string{"My-Remote-User", "SSO-User"}, }, }, } // Start server clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } // Use the server and CA info, but no client cert info anonConfig := kclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Build the authorize request with the My-Remote-User header authorizeURL := clientConfig.Host + "/oauth/authorize?client_id=openshift-challenging-client&response_type=token" req, err := http.NewRequest("GET", authorizeURL, nil) req.Header.Set("My-Remote-User", "myuser") // Make the request without cert auth transport, err := kclient.TransportFor(&anonConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } resp, err := transport.RoundTrip(req) if err != nil { t.Fatalf("unexpected error: %v", err) } redirect, err := resp.Location() if err != nil { t.Fatalf("expected 302 redirect, got error: %v", err) } if redirect.Query().Get("error") == "" { t.Fatalf("expected unsuccessful token request, got redirected to %v", redirect.String()) } // Use the server and CA info, with cert info authProxyConfig := anonConfig authProxyConfig.CertData = clientCert authProxyConfig.KeyData = clientKey // Make the request with cert info transport, err = kclient.TransportFor(&authProxyConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } resp, err = transport.RoundTrip(req) if err != nil { t.Fatalf("unexpected error: %v", err) } redirect, err = resp.Location() if err != nil { t.Fatalf("expected 302 redirect, got error: %v", err) } if redirect.Query().Get("error") != "" { t.Fatalf("expected successful token request, got error %v", redirect.String()) } // Extract the access_token // group #0 is everything. #1 #2 #3 accessTokenRedirectRegex := regexp.MustCompile(`(^|&)access_token=([^&]+)($|&)`) accessToken := "" if matches := accessTokenRedirectRegex.FindStringSubmatch(redirect.Fragment); matches != nil { accessToken = matches[2] } if accessToken == "" { t.Fatalf("Expected access token, got %s", redirect.String()) } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("Unexpected error: %v", err) } if user.Name != "myuser" { t.Fatalf("Expected myuser as the user, got %v", user) } }
func TestLogin(t *testing.T) { clientcmd.DefaultCluster = clientcmdapi.Cluster{Server: ""} _, clusterAdminKubeConfig, err := testutil.StartTestMaster() if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClient, err := testutil.GetClusterAdminClient(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } clusterAdminClientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Fatalf("unexpected error: %v", err) } username := "******" password := "******" project := "the-singularity-is-near" server := clusterAdminClientConfig.Host loginOptions := newLoginOptions(server, username, password, true) if err := loginOptions.GatherInfo(); err != nil { t.Fatalf("Error trying to determine server info: %v", err) } if loginOptions.Username != username { t.Fatalf("Unexpected user after authentication: %#v", loginOptions) } newProjectOptions := &newproject.NewProjectOptions{ Client: clusterAdminClient, ProjectName: project, AdminRole: bootstrappolicy.AdminRoleName, AdminUser: username, } if err := newProjectOptions.Run(false); err != nil { t.Fatalf("unexpected error, a project is required to continue: %v", err) } oClient, _ := client.New(loginOptions.Config) p, err := oClient.Projects().Get(project) if err != nil { t.Errorf("unexpected error: %v", err) } if p.Name != project { t.Fatalf("unexpected project: %#v", p) } // TODO Commented because of incorrectly hitting cache when listing projects. // Should be enabled again when cache eviction is properly fixed. // err = loginOptions.GatherProjectInfo() // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // if loginOptions.Project != project { // t.Fatalf("Expected project %v but got %v", project, loginOptions.Project) // } // configFile, err := ioutil.TempFile("", "openshiftconfig") // if err != nil { // t.Fatalf("unexpected error: %v", err) // } // defer os.Remove(configFile.Name()) // if _, err = loginOptions.SaveConfig(configFile.Name()); err != nil { // t.Fatalf("unexpected error: %v", err) // } userWhoamiOptions := cmd.WhoAmIOptions{oClient.Users(), ioutil.Discard} retrievedUser, err := userWhoamiOptions.WhoAmI() if err != nil { t.Errorf("unexpected error: %v", err) } if retrievedUser.Name != username { t.Errorf("expected %v, got %v", retrievedUser.Name, username) } adminWhoamiOptions := cmd.WhoAmIOptions{clusterAdminClient.Users(), ioutil.Discard} retrievedAdmin, err := adminWhoamiOptions.WhoAmI() if err != nil { t.Errorf("unexpected error: %v", err) } if retrievedAdmin.Name != "system:admin" { t.Errorf("expected %v, got %v", retrievedAdmin.Name, "system:admin") } }
func TestHTPasswd(t *testing.T) { htpasswdFile, err := ioutil.TempFile("", "test.htpasswd") if err != nil { t.Fatalf("unexpected error: %v", err) } defer os.Remove(htpasswdFile.Name()) masterOptions, err := testutil.DefaultMasterOptions() if err != nil { t.Fatalf("unexpected error: %v", err) } masterOptions.OAuthConfig.IdentityProviders[0] = configapi.IdentityProvider{ Name: "htpasswd", UseAsChallenger: true, UseAsLogin: true, Provider: runtime.EmbeddedObject{ &configapi.HTPasswdPasswordIdentityProvider{ File: htpasswdFile.Name(), }, }, } clusterAdminKubeConfig, err := testutil.StartConfiguredMaster(masterOptions) if err != nil { t.Fatalf("unexpected error: %v", err) } clientConfig, err := testutil.GetClusterAdminClientConfig(clusterAdminKubeConfig) if err != nil { t.Errorf("unexpected error: %v", err) } // Use the server and CA info anonConfig := kclient.Config{} anonConfig.Host = clientConfig.Host anonConfig.CAFile = clientConfig.CAFile anonConfig.CAData = clientConfig.CAData // Make sure we can't authenticate if _, err := tokencmd.RequestToken(&anonConfig, nil, "username", "password"); err == nil { t.Error("Expected error, got none") } // Update the htpasswd file with output of `htpasswd -n -b username password` userpass := "******" ioutil.WriteFile(htpasswdFile.Name(), []byte(userpass), os.FileMode(0600)) // Make sure we can get a token accessToken, err := tokencmd.RequestToken(&anonConfig, nil, "username", "password") if err != nil { t.Fatalf("Unexpected error: %v", err) } if len(accessToken) == 0 { t.Errorf("Expected access token, got none") } // Make sure we can use the token, and it represents who we expect userConfig := anonConfig userConfig.BearerToken = accessToken userClient, err := client.New(&userConfig) if err != nil { t.Fatalf("Unexpected error: %v", err) } user, err := userClient.Users().Get("~") if err != nil { t.Fatalf("Unexpected error: %v", err) } if user.Name != "username" { t.Fatalf("Expected username as the user, got %v", user) } }