...

Source file src/k8s.io/kubectl/pkg/cmd/auth/whoami.go

Documentation: k8s.io/kubectl/pkg/cmd/auth

     1  /*
     2  Copyright 2017 The Kubernetes Authors.
     3  
     4  Licensed under the Apache License, Version 2.0 (the "License");
     5  you may not use this file except in compliance with the License.
     6  You may obtain a copy of the License at
     7  
     8      http://www.apache.org/licenses/LICENSE-2.0
     9  
    10  Unless required by applicable law or agreed to in writing, software
    11  distributed under the License is distributed on an "AS IS" BASIS,
    12  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13  See the License for the specific language governing permissions and
    14  limitations under the License.
    15  */
    16  
    17  package auth
    18  
    19  import (
    20  	"context"
    21  	"fmt"
    22  	"io"
    23  
    24  	"github.com/spf13/cobra"
    25  	authenticationv1 "k8s.io/api/authentication/v1"
    26  	authenticationv1alpha1 "k8s.io/api/authentication/v1alpha1"
    27  	authenticationv1beta1 "k8s.io/api/authentication/v1beta1"
    28  	"k8s.io/apimachinery/pkg/api/errors"
    29  	metav1 "k8s.io/apimachinery/pkg/apis/meta/v1"
    30  	"k8s.io/apimachinery/pkg/runtime"
    31  	"k8s.io/apimachinery/pkg/util/sets"
    32  	"k8s.io/cli-runtime/pkg/genericclioptions"
    33  	"k8s.io/cli-runtime/pkg/genericiooptions"
    34  	"k8s.io/cli-runtime/pkg/printers"
    35  	authenticationv1client "k8s.io/client-go/kubernetes/typed/authentication/v1"
    36  	authenticationv1alpha1client "k8s.io/client-go/kubernetes/typed/authentication/v1alpha1"
    37  	authenticationv1beta1client "k8s.io/client-go/kubernetes/typed/authentication/v1beta1"
    38  	cmdutil "k8s.io/kubectl/pkg/cmd/util"
    39  	"k8s.io/kubectl/pkg/scheme"
    40  	"k8s.io/kubectl/pkg/util/templates"
    41  )
    42  
    43  // WhoAmIFlags directly reflect the information that CLI is gathering via flags.  They will be converted to Options, which
    44  // reflect the runtime requirements for the command.  This structure reduces the transformation to wiring and makes
    45  // the logic itself easy to unit test.
    46  type WhoAmIFlags struct {
    47  	RESTClientGetter genericclioptions.RESTClientGetter
    48  	PrintFlags       *genericclioptions.PrintFlags
    49  
    50  	genericiooptions.IOStreams
    51  }
    52  
    53  // NewWhoAmIFlags returns a default WhoAmIFlags.
    54  func NewWhoAmIFlags(restClientGetter genericclioptions.RESTClientGetter, streams genericiooptions.IOStreams) *WhoAmIFlags {
    55  	return &WhoAmIFlags{
    56  		RESTClientGetter: restClientGetter,
    57  		PrintFlags:       genericclioptions.NewPrintFlags("").WithTypeSetter(scheme.Scheme),
    58  		IOStreams:        streams,
    59  	}
    60  }
    61  
    62  // AddFlags registers flags for a cli.
    63  func (flags *WhoAmIFlags) AddFlags(cmd *cobra.Command) {
    64  	flags.PrintFlags.AddFlags(cmd)
    65  }
    66  
    67  // ToOptions converts from CLI inputs to runtime inputs.
    68  func (flags *WhoAmIFlags) ToOptions(ctx context.Context, args []string) (*WhoAmIOptions, error) {
    69  	w := &WhoAmIOptions{
    70  		ctx:       ctx,
    71  		IOStreams: flags.IOStreams,
    72  	}
    73  
    74  	clientConfig, err := flags.RESTClientGetter.ToRESTConfig()
    75  	if err != nil {
    76  		return nil, err
    77  	}
    78  
    79  	w.authV1alpha1Client, err = authenticationv1alpha1client.NewForConfig(clientConfig)
    80  	if err != nil {
    81  		return nil, err
    82  	}
    83  
    84  	w.authV1beta1Client, err = authenticationv1beta1client.NewForConfig(clientConfig)
    85  	if err != nil {
    86  		return nil, err
    87  	}
    88  
    89  	w.authV1Client, err = authenticationv1client.NewForConfig(clientConfig)
    90  	if err != nil {
    91  		return nil, err
    92  	}
    93  
    94  	if !flags.PrintFlags.OutputFlagSpecified() {
    95  		w.resourcePrinterFunc = printTableSelfSubjectAccessReview
    96  	} else {
    97  		printer, err := flags.PrintFlags.ToPrinter()
    98  		if err != nil {
    99  			return nil, err
   100  		}
   101  		w.resourcePrinterFunc = printer.PrintObj
   102  	}
   103  
   104  	return w, nil
   105  }
   106  
   107  // WhoAmIOptions is the start of the data required to perform the operation. As new fields are added,
   108  // add them here instead of referencing the cmd.Flags()
   109  type WhoAmIOptions struct {
   110  	authV1alpha1Client authenticationv1alpha1client.AuthenticationV1alpha1Interface
   111  	authV1beta1Client  authenticationv1beta1client.AuthenticationV1beta1Interface
   112  	authV1Client       authenticationv1client.AuthenticationV1Interface
   113  
   114  	ctx context.Context
   115  
   116  	resourcePrinterFunc printers.ResourcePrinterFunc
   117  
   118  	genericiooptions.IOStreams
   119  }
   120  
   121  var (
   122  	whoAmILong = templates.LongDesc(`
   123  		Experimental: Check who you are and your attributes (groups, extra).
   124  
   125          This command is helpful to get yourself aware of the current user attributes, 
   126          especially when dynamic authentication, e.g., token webhook, auth proxy, or OIDC provider, 
   127          is enabled in the Kubernetes cluster.
   128  	`)
   129  
   130  	whoAmIExample = templates.Examples(`
   131  		# Get your subject attributes.
   132  		kubectl auth whoami
   133  		
   134  		# Get your subject attributes in JSON format.
   135  		kubectl auth whoami -o json
   136  	`)
   137  )
   138  
   139  // NewCmdWhoAmI returns an initialized Command for 'auth whoami' sub command. Experimental.
   140  func NewCmdWhoAmI(restClientGetter genericclioptions.RESTClientGetter, streams genericiooptions.IOStreams) *cobra.Command {
   141  	flags := NewWhoAmIFlags(restClientGetter, streams)
   142  
   143  	cmd := &cobra.Command{
   144  		Use:                   "whoami",
   145  		DisableFlagsInUseLine: true,
   146  		Short:                 "Experimental: Check self subject attributes",
   147  		Long:                  whoAmILong,
   148  		Example:               whoAmIExample,
   149  		Run: func(cmd *cobra.Command, args []string) {
   150  			o, err := flags.ToOptions(cmd.Context(), args)
   151  			cmdutil.CheckErr(err)
   152  			cmdutil.CheckErr(o.Run())
   153  		},
   154  	}
   155  
   156  	flags.AddFlags(cmd)
   157  	return cmd
   158  }
   159  
   160  var (
   161  	notEnabledErr = fmt.Errorf(
   162  		"the selfsubjectreviews API is not enabled in the cluster\n" +
   163  			"enable APISelfSubjectReview feature gate and authentication.k8s.io/v1alpha1 or authentication.k8s.io/v1beta1 API")
   164  
   165  	forbiddenErr = fmt.Errorf(
   166  		"the selfsubjectreviews API is not enabled in the cluster or you do not have permission to call it")
   167  )
   168  
   169  // Run prints all user attributes.
   170  func (o WhoAmIOptions) Run() error {
   171  	var (
   172  		res runtime.Object
   173  		err error
   174  	)
   175  
   176  	res, err = o.authV1Client.
   177  		SelfSubjectReviews().
   178  		Create(context.TODO(), &authenticationv1.SelfSubjectReview{}, metav1.CreateOptions{})
   179  	if err != nil && errors.IsNotFound(err) {
   180  		// Fallback to Beta API if Beta is not enabled
   181  		res, err = o.authV1beta1Client.
   182  			SelfSubjectReviews().
   183  			Create(context.TODO(), &authenticationv1beta1.SelfSubjectReview{}, metav1.CreateOptions{})
   184  		if err != nil && errors.IsNotFound(err) {
   185  			// Fallback to Alpha API if Beta is not enabled
   186  			res, err = o.authV1alpha1Client.
   187  				SelfSubjectReviews().
   188  				Create(context.TODO(), &authenticationv1alpha1.SelfSubjectReview{}, metav1.CreateOptions{})
   189  		}
   190  	}
   191  	if err != nil {
   192  		switch {
   193  		case errors.IsForbidden(err):
   194  			return forbiddenErr
   195  		case errors.IsNotFound(err):
   196  			return notEnabledErr
   197  		default:
   198  			return err
   199  		}
   200  	}
   201  	return o.resourcePrinterFunc(res, o.Out)
   202  }
   203  
   204  func getUserInfo(obj runtime.Object) (authenticationv1.UserInfo, error) {
   205  	switch obj.(type) {
   206  	case *authenticationv1alpha1.SelfSubjectReview:
   207  		return obj.(*authenticationv1alpha1.SelfSubjectReview).Status.UserInfo, nil
   208  	case *authenticationv1beta1.SelfSubjectReview:
   209  		return obj.(*authenticationv1beta1.SelfSubjectReview).Status.UserInfo, nil
   210  	case *authenticationv1.SelfSubjectReview:
   211  		return obj.(*authenticationv1.SelfSubjectReview).Status.UserInfo, nil
   212  	default:
   213  		return authenticationv1.UserInfo{}, fmt.Errorf("unexpected response type %T, expected SelfSubjectReview", obj)
   214  	}
   215  }
   216  
   217  func printTableSelfSubjectAccessReview(obj runtime.Object, out io.Writer) error {
   218  	ui, err := getUserInfo(obj)
   219  	if err != nil {
   220  		return err
   221  	}
   222  
   223  	w := printers.GetNewTabWriter(out)
   224  	defer w.Flush()
   225  
   226  	_, err = fmt.Fprintf(w, "ATTRIBUTE\tVALUE\n")
   227  	if err != nil {
   228  		return fmt.Errorf("cannot write a header: %w", err)
   229  	}
   230  
   231  	if ui.Username != "" {
   232  		_, err := fmt.Fprintf(w, "Username\t%s\n", ui.Username)
   233  		if err != nil {
   234  			return fmt.Errorf("cannot write a username: %w", err)
   235  		}
   236  	}
   237  
   238  	if ui.UID != "" {
   239  		_, err := fmt.Fprintf(w, "UID\t%s\n", ui.UID)
   240  		if err != nil {
   241  			return fmt.Errorf("cannot write a uid: %w", err)
   242  		}
   243  	}
   244  
   245  	if len(ui.Groups) > 0 {
   246  		_, err := fmt.Fprintf(w, "Groups\t%v\n", ui.Groups)
   247  		if err != nil {
   248  			return fmt.Errorf("cannot write groups: %w", err)
   249  		}
   250  	}
   251  
   252  	if len(ui.Extra) > 0 {
   253  		for _, k := range sets.StringKeySet(ui.Extra).List() {
   254  			v := ui.Extra[k]
   255  			_, err := fmt.Fprintf(w, "Extra: %s\t%v\n", k, v)
   256  			if err != nil {
   257  				return fmt.Errorf("cannot write an extra: %w", err)
   258  			}
   259  		}
   260  
   261  	}
   262  	return nil
   263  }
   264  

View as plain text