diff --git a/hack/test-cmd.sh b/hack/test-cmd.sh index 5e67cc35ba27..8b16be367bf1 100755 --- a/hack/test-cmd.sh +++ b/hack/test-cmd.sh @@ -100,6 +100,7 @@ export OPENSHIFT_PROFILE="${CLI_PROFILE-}" [ "$(openshift ex)" ] [ "$(openshift ex config 2>&1)" ] [ "$(openshift ex tokens)" ] +[ "$(openshift ex policy 2>&1)" ] [ "$(openshift kubectl)" ] [ "$(openshift kube 2>&1)" ] @@ -168,3 +169,11 @@ osc cancel-build "${started}" --dump-logs --restart echo "cancel-build: ok" osc get minions,pods + +openshift ex policy add-group cluster-admin system:unauthenticated +openshift ex policy remove-group cluster-admin system:unauthenticated +openshift ex policy remove-group-from-project system:unauthenticated +openshift ex policy add-user cluster-admin system:no-user +openshift ex policy remove-user cluster-admin system:no-user +openshift ex policy remove-user-from-project system:no-user +echo "ex policy: ok" diff --git a/pkg/cmd/experimental/policy/add_group.go b/pkg/cmd/experimental/policy/add_group.go new file mode 100644 index 000000000000..b270e41a1f76 --- /dev/null +++ b/pkg/cmd/experimental/policy/add_group.go @@ -0,0 +1,102 @@ +package policy + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + + authorizationapi "github.com/openshift/origin/pkg/authorization/api" + "github.com/openshift/origin/pkg/client" +) + +type addGroupOptions struct { + roleNamespace string + roleName string + clientConfig clientcmd.ClientConfig + + groupNames []string +} + +func NewCmdAddGroup(clientConfig clientcmd.ClientConfig) *cobra.Command { + options := &addGroupOptions{clientConfig: clientConfig} + + cmd := &cobra.Command{ + Use: "add-group [group]...", + Short: "add group to role", + Long: `add group to role`, + Run: func(cmd *cobra.Command, args []string) { + if !options.complete(cmd) { + return + } + + err := options.run() + if err != nil { + fmt.Printf("%v\n", err) + } + }, + } + + cmd.Flags().StringVar(&options.roleNamespace, "role-namespace", "master", "namespace where the role is located.") + + return cmd +} + +func (o *addGroupOptions) complete(cmd *cobra.Command) bool { + args := cmd.Flags().Args() + if len(args) < 2 { + cmd.Help() + return false + } + + o.roleName = args[0] + o.groupNames = args[1:] + return true +} + +func (o *addGroupOptions) run() error { + clientConfig, err := o.clientConfig.ClientConfig() + if err != nil { + return err + } + client, err := client.New(clientConfig) + if err != nil { + return err + } + namespace, err := o.clientConfig.Namespace() + if err != nil { + return err + } + + roleBinding, roleBindingNames, err := getExistingRoleBindingForRole(o.roleNamespace, o.roleName, namespace, client) + if err != nil { + return err + } + isUpdate := true + if roleBinding == nil { + roleBinding = &authorizationapi.RoleBinding{} + isUpdate = false + } + + roleBinding.RoleRef.Namespace = o.roleNamespace + roleBinding.RoleRef.Name = o.roleName + + groups := util.StringSet{} + groups.Insert(roleBinding.GroupNames...) + groups.Insert(o.groupNames...) + roleBinding.GroupNames = groups.List() + + if isUpdate { + _, err = client.RoleBindings(namespace).Update(roleBinding) + } else { + roleBinding.Name = getUniqueName(o.roleName, roleBindingNames) + _, err = client.RoleBindings(namespace).Create(roleBinding) + } + if err != nil { + return err + } + + return nil +} diff --git a/pkg/cmd/experimental/policy/add_user.go b/pkg/cmd/experimental/policy/add_user.go new file mode 100644 index 000000000000..2843122f270e --- /dev/null +++ b/pkg/cmd/experimental/policy/add_user.go @@ -0,0 +1,102 @@ +package policy + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + + authorizationapi "github.com/openshift/origin/pkg/authorization/api" + "github.com/openshift/origin/pkg/client" +) + +type addUserOptions struct { + roleNamespace string + roleName string + clientConfig clientcmd.ClientConfig + + userNames []string +} + +func NewCmdAddUser(clientConfig clientcmd.ClientConfig) *cobra.Command { + options := &addUserOptions{clientConfig: clientConfig} + + cmd := &cobra.Command{ + Use: "add-user [user]...", + Short: "add user to role", + Long: `add user to role`, + Run: func(cmd *cobra.Command, args []string) { + if !options.complete(cmd) { + return + } + + err := options.run() + if err != nil { + fmt.Printf("%v\n", err) + } + }, + } + + cmd.Flags().StringVar(&options.roleNamespace, "role-namespace", "master", "namespace where the role is located.") + + return cmd +} + +func (o *addUserOptions) complete(cmd *cobra.Command) bool { + args := cmd.Flags().Args() + if len(args) < 2 { + cmd.Help() + return false + } + + o.roleName = args[0] + o.userNames = args[1:] + return true +} + +func (o *addUserOptions) run() error { + clientConfig, err := o.clientConfig.ClientConfig() + if err != nil { + return err + } + client, err := client.New(clientConfig) + if err != nil { + return err + } + namespace, err := o.clientConfig.Namespace() + if err != nil { + return err + } + + roleBinding, roleBindingNames, err := getExistingRoleBindingForRole(o.roleNamespace, o.roleName, namespace, client) + if err != nil { + return err + } + isUpdate := true + if roleBinding == nil { + roleBinding = &authorizationapi.RoleBinding{} + isUpdate = false + } + + roleBinding.RoleRef.Namespace = o.roleNamespace + roleBinding.RoleRef.Name = o.roleName + + users := util.StringSet{} + users.Insert(roleBinding.UserNames...) + users.Insert(o.userNames...) + roleBinding.UserNames = users.List() + + if isUpdate { + _, err = client.RoleBindings(namespace).Update(roleBinding) + } else { + roleBinding.Name = getUniqueName(o.roleName, roleBindingNames) + _, err = client.RoleBindings(namespace).Create(roleBinding) + } + if err != nil { + return err + } + + return nil +} diff --git a/pkg/cmd/experimental/policy/policy.go b/pkg/cmd/experimental/policy/policy.go new file mode 100644 index 000000000000..6ab9dbea7af4 --- /dev/null +++ b/pkg/cmd/experimental/policy/policy.go @@ -0,0 +1,101 @@ +package policy + +import ( + "fmt" + "os" + "strings" + + "github.com/golang/glog" + "github.com/spf13/cobra" + "github.com/spf13/pflag" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + + authorizationapi "github.com/openshift/origin/pkg/authorization/api" + "github.com/openshift/origin/pkg/client" +) + +func NewCommandPolicy(name string) *cobra.Command { + // Parent command to which all subcommands are added. + cmds := &cobra.Command{ + Use: name, + Short: "manage authorization policy", + Long: `manage authorization policy`, + Run: runHelp, + } + + // Override global default to https and port 8443 + clientcmd.DefaultCluster.Server = "https://localhost:8443" + clientConfig := defaultClientConfig(cmds.PersistentFlags()) + + cmds.AddCommand(NewCmdAddUser(clientConfig)) + cmds.AddCommand(NewCmdRemoveUser(clientConfig)) + cmds.AddCommand(NewCmdRemoveUserFromProject(clientConfig)) + cmds.AddCommand(NewCmdAddGroup(clientConfig)) + cmds.AddCommand(NewCmdRemoveGroup(clientConfig)) + cmds.AddCommand(NewCmdRemoveGroupFromProject(clientConfig)) + + return cmds +} + +func runHelp(cmd *cobra.Command, args []string) { + cmd.Help() +} + +func getFlagString(cmd *cobra.Command, flag string) string { + f := cmd.Flags().Lookup(flag) + if f == nil { + glog.Fatalf("Flag accessed but not defined for command %s: %s", cmd.Name(), flag) + } + return f.Value.String() +} + +// Copy of kubectl/cmd/DefaultClientConfig, using NewNonInteractiveDeferredLoadingClientConfig +func defaultClientConfig(flags *pflag.FlagSet) clientcmd.ClientConfig { + loadingRules := clientcmd.NewClientConfigLoadingRules() + loadingRules.EnvVarPath = os.Getenv(clientcmd.RecommendedConfigPathEnvVar) + flags.StringVar(&loadingRules.CommandLinePath, "kubeconfig", "", "Path to the kubeconfig file to use for CLI requests.") + + overrides := &clientcmd.ConfigOverrides{} + clientcmd.BindOverrideFlags(overrides, flags, clientcmd.RecommendedConfigOverrideFlags("")) + clientConfig := clientcmd.NewNonInteractiveDeferredLoadingClientConfig(loadingRules, overrides) + + return clientConfig +} + +func getUniqueName(basename string, existingNames *util.StringSet) string { + if !existingNames.Has(basename) { + return basename + } + + for i := 0; i < 100; i++ { + trialName := fmt.Sprintf("%v-%d", basename, i) + if !existingNames.Has(trialName) { + return trialName + } + } + + return string(util.NewUUID()) +} + +func getExistingRoleBindingForRole(roleNamespace, role, bindingNamespace string, client *client.Client) (*authorizationapi.RoleBinding, *util.StringSet, error) { + existingBindings, err := client.PolicyBindings(bindingNamespace).Get(roleNamespace) + if err != nil && !strings.Contains(err.Error(), " not found") { + return nil, &util.StringSet{}, err + } + + roleBindingNames := &util.StringSet{} + roleBinding := (*authorizationapi.RoleBinding)(nil) + // see if we can find an existing binding that points to the role in question. + for _, currBinding := range existingBindings.RoleBindings { + roleBindingNames.Insert(currBinding.Name) + + if currBinding.RoleRef.Name == role { + t := currBinding + roleBinding = &t + } + } + + return roleBinding, roleBindingNames, nil +} diff --git a/pkg/cmd/experimental/policy/remove_group.go b/pkg/cmd/experimental/policy/remove_group.go new file mode 100644 index 000000000000..bc365c746796 --- /dev/null +++ b/pkg/cmd/experimental/policy/remove_group.go @@ -0,0 +1,91 @@ +package policy + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + + "github.com/openshift/origin/pkg/client" +) + +type removeGroupOptions struct { + roleNamespace string + roleName string + clientConfig clientcmd.ClientConfig + + groupNames []string +} + +func NewCmdRemoveGroup(clientConfig clientcmd.ClientConfig) *cobra.Command { + options := &removeGroupOptions{clientConfig: clientConfig} + + cmd := &cobra.Command{ + Use: "remove-group [group]...", + Short: "remove group from role", + Long: `remove group from role`, + Run: func(cmd *cobra.Command, args []string) { + if !options.complete(cmd) { + return + } + + err := options.run() + if err != nil { + fmt.Printf("%v\n", err) + } + }, + } + + cmd.Flags().StringVar(&options.roleNamespace, "role-namespace", "master", "namespace where the role is located.") + + return cmd +} + +func (o *removeGroupOptions) complete(cmd *cobra.Command) bool { + args := cmd.Flags().Args() + if len(args) < 2 { + cmd.Help() + return false + } + + o.roleName = args[0] + o.groupNames = args[1:] + return true +} + +func (o *removeGroupOptions) run() error { + clientConfig, err := o.clientConfig.ClientConfig() + if err != nil { + return err + } + client, err := client.New(clientConfig) + if err != nil { + return err + } + namespace, err := o.clientConfig.Namespace() + if err != nil { + return err + } + + roleBinding, _, err := getExistingRoleBindingForRole(o.roleNamespace, o.roleName, namespace, client) + if err != nil { + return err + } + if roleBinding == nil { + return fmt.Errorf("unable to locate RoleBinding for %v::%v in %v", o.roleNamespace, o.roleName, namespace) + } + + groups := util.StringSet{} + groups.Insert(roleBinding.GroupNames...) + groups.Delete(o.groupNames...) + roleBinding.GroupNames = groups.List() + + _, err = client.RoleBindings(namespace).Update(roleBinding) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/cmd/experimental/policy/remove_group_from_project.go b/pkg/cmd/experimental/policy/remove_group_from_project.go new file mode 100644 index 000000000000..4086053b1477 --- /dev/null +++ b/pkg/cmd/experimental/policy/remove_group_from_project.go @@ -0,0 +1,89 @@ +package policy + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" + klabels "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + + "github.com/openshift/origin/pkg/client" +) + +type removeGroupFromProjectOptions struct { + clientConfig clientcmd.ClientConfig + + groupNames []string +} + +func NewCmdRemoveGroupFromProject(clientConfig clientcmd.ClientConfig) *cobra.Command { + options := &removeGroupFromProjectOptions{clientConfig: clientConfig} + + cmd := &cobra.Command{ + Use: "remove-group-from-project [group]...", + Short: "remove group from project", + Long: `remove group from project`, + Run: func(cmd *cobra.Command, args []string) { + if !options.complete(cmd) { + return + } + + err := options.run() + if err != nil { + fmt.Printf("%v\n", err) + } + }, + } + + return cmd +} + +func (o *removeGroupFromProjectOptions) complete(cmd *cobra.Command) bool { + args := cmd.Flags().Args() + if len(args) < 1 { + cmd.Help() + return false + } + + o.groupNames = args + return true +} + +func (o *removeGroupFromProjectOptions) run() error { + clientConfig, err := o.clientConfig.ClientConfig() + if err != nil { + return err + } + client, err := client.New(clientConfig) + if err != nil { + return err + } + namespace, err := o.clientConfig.Namespace() + if err != nil { + return err + } + + bindingList, err := client.PolicyBindings(namespace).List(klabels.Everything(), klabels.Everything()) + if err != nil { + return err + } + + for _, currBindings := range bindingList.Items { + for _, currBinding := range currBindings.RoleBindings { + groupsForBinding := util.StringSet{} + groupsForBinding.Insert(currBinding.GroupNames...) + groupsForBinding.Delete(o.groupNames...) + + currBinding.GroupNames = groupsForBinding.List() + + _, err = client.RoleBindings(namespace).Update(&currBinding) + if err != nil { + return err + } + } + } + + return nil +} diff --git a/pkg/cmd/experimental/policy/remove_user.go b/pkg/cmd/experimental/policy/remove_user.go new file mode 100644 index 000000000000..256ff69336f9 --- /dev/null +++ b/pkg/cmd/experimental/policy/remove_user.go @@ -0,0 +1,91 @@ +package policy + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + + "github.com/openshift/origin/pkg/client" +) + +type removeUserOptions struct { + roleNamespace string + roleName string + clientConfig clientcmd.ClientConfig + + userNames []string +} + +func NewCmdRemoveUser(clientConfig clientcmd.ClientConfig) *cobra.Command { + options := &removeUserOptions{clientConfig: clientConfig} + + cmd := &cobra.Command{ + Use: "remove-user [user]...", + Short: "remove user from role", + Long: `remove user from role`, + Run: func(cmd *cobra.Command, args []string) { + if !options.complete(cmd) { + return + } + + err := options.run() + if err != nil { + fmt.Printf("%v\n", err) + } + }, + } + + cmd.Flags().StringVar(&options.roleNamespace, "role-namespace", "master", "namespace where the role is located.") + + return cmd +} + +func (o *removeUserOptions) complete(cmd *cobra.Command) bool { + args := cmd.Flags().Args() + if len(args) < 2 { + cmd.Help() + return false + } + + o.roleName = args[0] + o.userNames = args[1:] + return true +} + +func (o *removeUserOptions) run() error { + clientConfig, err := o.clientConfig.ClientConfig() + if err != nil { + return err + } + client, err := client.New(clientConfig) + if err != nil { + return err + } + namespace, err := o.clientConfig.Namespace() + if err != nil { + return err + } + + roleBinding, _, err := getExistingRoleBindingForRole(o.roleNamespace, o.roleName, namespace, client) + if err != nil { + return err + } + if roleBinding == nil { + return fmt.Errorf("unable to locate RoleBinding for %v::%v in %v", o.roleNamespace, o.roleName, namespace) + } + + users := util.StringSet{} + users.Insert(roleBinding.UserNames...) + users.Delete(o.userNames...) + roleBinding.UserNames = users.List() + + _, err = client.RoleBindings(namespace).Update(roleBinding) + if err != nil { + return err + } + + return nil +} diff --git a/pkg/cmd/experimental/policy/remove_user_from_project.go b/pkg/cmd/experimental/policy/remove_user_from_project.go new file mode 100644 index 000000000000..496b257827f4 --- /dev/null +++ b/pkg/cmd/experimental/policy/remove_user_from_project.go @@ -0,0 +1,89 @@ +package policy + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/GoogleCloudPlatform/kubernetes/pkg/client/clientcmd" + klabels "github.com/GoogleCloudPlatform/kubernetes/pkg/labels" + "github.com/GoogleCloudPlatform/kubernetes/pkg/util" + + "github.com/openshift/origin/pkg/client" +) + +type removeUserFromProjectOptions struct { + clientConfig clientcmd.ClientConfig + + userNames []string +} + +func NewCmdRemoveUserFromProject(clientConfig clientcmd.ClientConfig) *cobra.Command { + options := &removeUserFromProjectOptions{clientConfig: clientConfig} + + cmd := &cobra.Command{ + Use: "remove-user-from-project [user]...", + Short: "remove user from project", + Long: `remove user from project`, + Run: func(cmd *cobra.Command, args []string) { + if !options.complete(cmd) { + return + } + + err := options.run() + if err != nil { + fmt.Printf("%v\n", err) + } + }, + } + + return cmd +} + +func (o *removeUserFromProjectOptions) complete(cmd *cobra.Command) bool { + args := cmd.Flags().Args() + if len(args) < 1 { + cmd.Help() + return false + } + + o.userNames = args + return true +} + +func (o *removeUserFromProjectOptions) run() error { + clientConfig, err := o.clientConfig.ClientConfig() + if err != nil { + return err + } + client, err := client.New(clientConfig) + if err != nil { + return err + } + namespace, err := o.clientConfig.Namespace() + if err != nil { + return err + } + + bindingList, err := client.PolicyBindings(namespace).List(klabels.Everything(), klabels.Everything()) + if err != nil { + return err + } + + for _, currBindings := range bindingList.Items { + for _, currBinding := range currBindings.RoleBindings { + usersForBinding := util.StringSet{} + usersForBinding.Insert(currBinding.UserNames...) + usersForBinding.Delete(o.userNames...) + + currBinding.UserNames = usersForBinding.List() + + _, err = client.RoleBindings(namespace).Update(&currBinding) + if err != nil { + return err + } + } + } + + return nil +} diff --git a/pkg/cmd/openshift/openshift.go b/pkg/cmd/openshift/openshift.go index 762c7393d27f..c7675cfe824f 100644 --- a/pkg/cmd/openshift/openshift.go +++ b/pkg/cmd/openshift/openshift.go @@ -9,6 +9,7 @@ import ( "github.com/openshift/origin/pkg/cmd/cli" "github.com/openshift/origin/pkg/cmd/experimental/config" + "github.com/openshift/origin/pkg/cmd/experimental/policy" "github.com/openshift/origin/pkg/cmd/experimental/tokens" "github.com/openshift/origin/pkg/cmd/flagtypes" "github.com/openshift/origin/pkg/cmd/infra/builder" @@ -110,6 +111,7 @@ func newExperimentalCommand(parentName, name string) *cobra.Command { } experimental.AddCommand(config.NewCmdConfig(fmt.Sprintf("%s %s", parentName, name), "config")) experimental.AddCommand(tokens.NewCmdTokens("tokens")) + experimental.AddCommand(policy.NewCommandPolicy("policy")) return experimental }