From 9dc381c0a35be9449390a32f239167eb42aebefa Mon Sep 17 00:00:00 2001 From: Gregory Ballantine Date: Sun, 15 Jul 2018 23:27:27 -0400 Subject: [PATCH] Added group subcommand with add and delete commands --- cmd/group.go | 181 ++++++++++++++++++++++++++++++++++++++++++++++ lib/util/slice.go | 12 +++ 2 files changed, 193 insertions(+) create mode 100644 cmd/group.go create mode 100644 lib/util/slice.go diff --git a/cmd/group.go b/cmd/group.go new file mode 100644 index 0000000..95cf93d --- /dev/null +++ b/cmd/group.go @@ -0,0 +1,181 @@ +package cmd + +import ( + "errors" + "fmt" + "log" + "os" + "strconv" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/viper" + ldap "gopkg.in/ldap.v2" + + util "git.metaunix.net/metaunix.net/muldap/lib/util" +) + +var ( + // group subcommand arguments + flagGroupName string + flagGroupType string + flagGroupIdNumber int + flagGroupMembers string + + validTypes []string = []string{"app", "unix"} +) + +func init() { + // define group add subcommand flags + groupAddCmd.Flags().StringP("base_ou", "o", "", "LDAP OU to create the new group entry under") + groupAddCmd.Flags().StringP("id_attribute", "a", "cn", "LDAP DN attribute for groups") + groupAddCmd.Flags().StringVarP(&flagGroupName, "group_name", "n", "", "Name for the new group") + groupAddCmd.Flags().StringVarP(&flagGroupType, "group_type", "t", "unix", "Type of the new group") + groupAddCmd.Flags().IntVarP(&flagGroupIdNumber, "group_id", "i", -1, "ID number of the new group (Unix/Posix groups only)") + groupAddCmd.Flags().StringVarP(&flagGroupMembers, "group_members", "m", "", "Members to add to the newly created group") + // bind config file values to group add flags + viper.BindPFlag("group.base_ou", groupAddCmd.Flags().Lookup("base_ou")) + viper.BindPFlag("group.id_attr", groupAddCmd.Flags().Lookup("id_attribute")) + + // define group delete subcommand flags + groupDeleteCmd.Flags().StringP("base_ou", "o", "", "LDAP OU where your group entries are stored") + groupDeleteCmd.Flags().StringP("id_attribute", "a", "cn", "LDAP DN attribute for groups") + groupDeleteCmd.Flags().StringVarP(&flagGroupName, "groupname", "n", "", "Username of group to delete") + // bind config file values to group delete flags + viper.BindPFlag("group.base_ou", groupDeleteCmd.Flags().Lookup("base_ou")) + viper.BindPFlag("group.id_attr", groupDeleteCmd.Flags().Lookup("id_attribute")) + + // register add command and subcommands + groupCmd.AddCommand(groupAddCmd, groupDeleteCmd) + rootCmd.AddCommand(groupCmd) +} + +// define group command +var groupCmd = &cobra.Command{ + Use: "group", + Short: "Manage LDAP group resources", + Long: `Perform various LDAP operations on group resources.`, + Run: func(cmd *cobra.Command, args []string) { + fmt.Println("Metaunix.net LDAP tool, group command. Available subcommands are: add, delete") + }, +} + +// define group subcommand +var groupAddCmd = &cobra.Command{ + Use: "add", + Short: "Add an LDAP group to the directory", + Long: `Create and add an LDAP group resource to your directory.`, + Run: func(cmd *cobra.Command, args []string) { + // check the type given + if !util.ContainsString(validTypes, flagGroupType) { + log.Fatal(errors.New(fmt.Sprintf("%s is not a valid group type; valid types are: %v", flagGroupType, validTypes))) + os.Exit(1) + } + + // create new LDAP connection + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", viper.GetString("host"), viper.GetInt("port"))) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + // bind as the admin group + err = l.Bind(viper.GetString("bind_dn"), viper.GetString("bind_pw")) + if err != nil { + log.Fatal(err) + } + + // set up group attributes + groupDn := fmt.Sprintf("%s=%s,%s", viper.GetString("group.id_attr"), flagGroupName, viper.GetString("group.base_ou")) + groupOClasses := viper.GetStringSlice("group.object_class") + + // create a new add request object + addRequest := ldap.NewAddRequest(groupDn) + // add group attributes to the request + addRequest.Attribute(viper.GetString("group.id_attr"), []string{flagGroupName}) + + // add attributes based on group type + if flagGroupType == "unix" { + // add unix group type to objectClasses + groupOClasses = append(groupOClasses, "posixGroup") + + // finish adding attributes to the request + addRequest.Attribute("objectClass", groupOClasses) + addRequest.Attribute("gidNumber", []string{strconv.Itoa(flagGroupIdNumber)}) + + if flagGroupMembers != "" { + addRequest.Attribute("memberUid", strings.Split(flagGroupMembers, ",")) + } else { + addRequest.Attribute("memberUid", []string{"noname"}) + } + } else if flagGroupType == "app" { + // add unix group type to objectClasses + groupOClasses = append(groupOClasses, "groupOfNames") + + // finish adding attributes to the request + addRequest.Attribute("objectClass", groupOClasses) + + if flagGroupMembers != "" { + groupMembers := strings.Split(flagGroupMembers, ",") + for i, member := range groupMembers { + groupMembers[i] = fmt.Sprintf("%s=%s,%s", viper.GetString("user.uid_attr"), member, viper.GetString("user.base_ou")) + } + addRequest.Attribute("member", groupMembers) + } else { + addRequest.Attribute("member", []string{fmt.Sprintf("%s=noname,%s", viper.GetString("user.uid_attr"), viper.GetString("user.base_ou"))}) + } + } + + // perform the add operation + err = l.Add(addRequest) + if err != nil { + log.Fatal(err) + } + + // create and send a modify request for each user to update modification times for replication + if flagGroupMembers != "" && flagGroupType == "app" { + groupMembers := strings.Split(flagGroupMembers, ",") + for _, member := range groupMembers { + modify := ldap.NewModifyRequest(fmt.Sprintf("%s=%s,%s", viper.GetString("user.uid_attr"), member, viper.GetString("user.base_ou"))) + modify.Replace(viper.GetString("user.uid_attr"), []string{member}) + err = l.Modify(modify) + if err != nil { + log.Fatal(err) + } + } + } + }, +} + +// define group delete subcommand +var groupDeleteCmd = &cobra.Command{ + Use: "delete", + Short: "Delete an LDAP group from the directory", + Long: `Delete an LDAP group resource from the directory.`, + Run: func(cmd *cobra.Command, args []string) { + // create new LDAP connection + l, err := ldap.Dial("tcp", fmt.Sprintf("%s:%d", viper.GetString("host"), viper.GetInt("port"))) + if err != nil { + log.Fatal(err) + } + defer l.Close() + + // bind as the admin group + err = l.Bind(viper.GetString("bind_dn"), viper.GetString("bind_pw")) + if err != nil { + log.Fatal(err) + } + + // set up group DN + groupDn := fmt.Sprintf("%s=%s,%s", viper.GetString("group.id_attr"), flagGroupName, viper.GetString("group.base_ou")) + + // create a new delete request object + deleteRequest := ldap.NewDelRequest(groupDn, []ldap.Control{}) + + // perform the delete operation + err = l.Del(deleteRequest) + if err != nil { + log.Fatal(err) + } + }, +} diff --git a/lib/util/slice.go b/lib/util/slice.go new file mode 100644 index 0000000..7e07828 --- /dev/null +++ b/lib/util/slice.go @@ -0,0 +1,12 @@ +package util + +// returns true if the needle exists in the haystack +func ContainsString(haystack []string, needle string) bool { + for _, val := range haystack { + if val == needle { + return true + } + } + + return false +}