Skip to content
Snippets Groups Projects
Select Git revision
  • main default protected
  • jupyterhub
  • artifacts
  • freeze-ssh-keys
  • fix-org-policy-unreg
  • email-users
  • fix-portalops-logout
  • stream-status
  • registries
  • e2mtest
  • xdc-org-mounts
  • storage
  • nilaway
  • grpc-web-service-auth
  • bjk-dev
  • model-branch-update
  • xdc-base-ub2404
  • small-regex
  • xdc-idempotence
  • next protected
  • v1.3.34 protected
  • v1.3.33 protected
  • v1.3.32 protected
  • v1.3.31 protected
  • v1.3.30 protected
  • v1.3.29 protected
  • v1.3.28 protected
  • v1.3.27 protected
  • v1.3.26 protected
  • v1.3.25 protected
  • v1.3.24 protected
  • v1.3.23 protected
  • v1.3.22 protected
  • v1.3.21 protected
  • v1.3.20 protected
  • v1.3.19 protected
  • v1.3.18 protected
  • v1.3.17 protected
  • v1.3.16 protected
  • v1.3.16-rc2d protected
40 results

group.go

Code owners
Assign users and groups as approvers for specific file changes. Learn more.
group.go 4.31 KiB
package workspace

import (
	"fmt"
	"io/ioutil"
	"os"
	"sort"
	"strconv"
	"strings"

	log "github.com/sirupsen/logrus"
)

// Group describes an /etc/group style file.
type Group struct {
	File    *SharedFile
	Entries map[string]*GroupEntry
}

// GroupEntry describes an entry in a group file.
type GroupEntry struct {
	Name  string
	Id    int
	Users []string
}

// OpenGroup - open the group file.
func OpenGroup(file string) (*Group, error) {

	sf, err := SharedOpen(file)
	if err != nil {
		return nil, err
	}

	group := &Group{
		File:    sf,
		Entries: make(map[string]*GroupEntry),
	}

	err = group.read()
	if err != nil {
		group.Close()
		return nil, err
	}

	return group, nil

}

func (g *Group) read() error {
	if _, err := os.Stat(g.File.Path); !os.IsNotExist(err) {
		src, err := ioutil.ReadFile(g.File.Path)
		if err != nil {
			return fmt.Errorf("could not read %s", err)
		}

		lines := strings.Split(string(src), "\n")

		for _, l := range lines {
			e := readGroupEntry(l)
			if e == nil {
				continue
			}
			g.Entries[e.Name] = e
		}
	}

	return nil
}
func readGroupEntry(line string) *GroupEntry {

	// example entries:
	// adm:x:4:syslog,glawler
	// root:x:0:
	if line == "" {
		return nil
	}

	parts := strings.Split(line, ":")
	if len(parts) < 3 {
		log.WithFields(log.Fields{"entry": line}).Warn("invalid group entry")
		return nil
	}

	id, err := strconv.Atoi(parts[2])
	if err != nil {
		log.WithFields(log.Fields{"entry": line}).Warn("invalid group id")
		return nil
	}

	ge := &GroupEntry{
		Name: parts[0],
		Id:   id,
	}

	if parts[3] != "" {
		for _, u := range strings.Split(parts[3], ",") {
			ge.Users = append(ge.Users, u)
		}
	}

	return ge
}

func (e *GroupEntry) Write() string {
	return fmt.Sprintf("%s:x:%d:%s", e.Name, e.Id, strings.Join(e.Users, ","))
}

func (e *GroupEntry) userExists(user string) bool {
	for _, u := range e.Users {
		if u == user {
			return true
		}
	}
	return false
}

// Merge two group entries. The passed in entry takes precedence if there is a clash. Names must match though.
func (ge *GroupEntry) Merge(b *GroupEntry) error {
	if ge.Name != b.Name {
		return fmt.Errorf("bad group entry merge")
	}

	// merge users, cutting out duplicates
	for _, u := range b.Users {
		if !ge.userExists(u) {
			ge.Users = append(ge.Users, u)
		}
	}
	return nil
}

// Merge two Groups. The passed in takes precedence when there is a clash.
func (g *Group) Merge(other *Group) error {
	for _, oe := range other.Entries {
		if ge, ok := g.Entries[oe.Name]; ok { // merge dup entry
			err := ge.Merge(oe)
			if err != nil {
				return err
			}
		} else {
			g.Entries[oe.Name] = oe
		}
	}
	return nil
}

func (g *Group) Write() error {

	src := g.String()
	return ioutil.WriteFile(g.File.Path, []byte(src), 0644)
}

// AddUser adds the given user to an exising group.
func (g *Group) AddUser(group, user string) error {
	entry, ok := g.Entries[group]
	if !ok {
		return fmt.Errorf("cannot add user %s: no such group %s", user, group)
	}
	// add the user if it doesn't already exist.
	if !entry.userExists(user) {
		entry.Users = append(entry.Users, user)
	}
	return nil
}

// GetEntry retrives a group entry by name.
func (g *Group) GetEntry(name string) (*GroupEntry, error) {
	entry, ok := g.Entries[name]
	if !ok {
		return nil, fmt.Errorf("no such group: %s", name)
	}
	return entry, nil
}

// AddEntry will add or overwrite an existing entry to the Group
func (g *Group) AddEntry(e *GroupEntry) {
	g.Entries[e.Name] = e
}

func (g *Group) String() string {
	var entries []*GroupEntry
	for _, e := range g.Entries {
		entries = append(entries, e)
	}

	sort.Slice(entries, func(i, j int) bool {
		return entries[i].Id < entries[j].Id
	})

	var src string
	for _, e := range entries {
		src += e.Write() + "\n"
	}
	return src
}

// Close - close the group file.
func (g *Group) Close() {
	g.File.Close()
}

// AddAllToGroup adds all accounts in the passwd given to the group given in the etc/group file specified.
func (g *Group) AddAllToGroup(group, passwdpath string) error {

	if _, ok := g.Entries[group]; !ok {
		return fmt.Errorf("non existent group")
	}
	p, err := OpenPasswd(passwdpath)
	if err != nil {
		return fmt.Errorf("bad passwd path")
	}
	defer p.Close()

	for _, pe := range p.Entries {
		err = g.AddUser(group, pe.Username)
		if err != nil {
			return fmt.Errorf("error adding user %s to group %s", pe.Username, group)
		}
	}
	return nil
}