1
0
Fork 0
mirror of https://github.com/Luzifer/password.git synced 2024-11-10 10:20:01 +00:00
password/vendor/github.com/tredoe/osutil/user/group.go

495 lines
9.7 KiB
Go
Raw Normal View History

// Copyright 2010 Jonas mg
//
// This Source Code Form is subject to the terms of the Mozilla Public
// License, v. 2.0. If a copy of the MPL was not distributed with this
// file, You can obtain one at http://mozilla.org/MPL/2.0/.
package user
import (
"errors"
"fmt"
"os"
"reflect"
"strconv"
"strings"
)
type groupField int
// Field names for group database.
const (
G_NAME groupField = 1 << iota
G_PASSWD
G_GID
G_MEMBER
G_ALL
)
func (f groupField) String() string {
switch f {
case G_NAME:
return "Name"
case G_PASSWD:
return "Passwd"
case G_GID:
return "GID"
case G_MEMBER:
return "Member"
}
return "ALL"
}
// A Group represents the format of a group on the system.
type Group struct {
// Group name. (Unique)
Name string
// Hashed password
//
// The (hashed) group password. If this field is empty, no password is needed.
password string
// The numeric group ID. (Unique)
GID int
// User list
//
// A list of the usernames that are members of this group, separated by commas.
UserList []string
addSystemGroup bool
}
// AddGroup returns a new Group.
func NewGroup(name string, members ...string) *Group {
return &Group{
Name: name,
password: "",
GID: -1,
UserList: members,
}
}
// NewSystemGroup adds a system group.
func NewSystemGroup(name string, members ...string) *Group {
return &Group{
Name: name,
password: "",
GID: -1,
UserList: members,
addSystemGroup: true,
}
}
func (g *Group) filename() string { return _GROUP_FILE }
// IsOfSystem indicates whether it is a system group.
func (g *Group) IsOfSystem() bool {
//loadConfig()
if g.GID > config.login.SYS_GID_MIN && g.GID < config.login.SYS_GID_MAX {
return true
}
return false
}
func (g *Group) String() string {
return fmt.Sprintf("%s:%s:%d:%s\n",
g.Name, g.password, g.GID, strings.Join(g.UserList, ","))
}
// parseGroup parses the row of a group.
func parseGroup(row string) (*Group, error) {
fields := strings.Split(row, ":")
if len(fields) != 4 {
return nil, rowError{_GROUP_FILE, row}
}
gid, err := strconv.Atoi(fields[2])
if err != nil {
return nil, atoiError{_GROUP_FILE, row, "GID"}
}
return &Group{
Name: fields[0],
password: fields[1],
GID: gid,
UserList: strings.Split(fields[3], ","),
}, nil
}
// == Lookup
//
// lookUp parses the group line searching a value into the field.
// Returns nil if it is not found.
func (*Group) lookUp(line string, f field, value interface{}) interface{} {
_field := f.(groupField)
allField := strings.Split(line, ":")
arrayField := make(map[int][]string)
intField := make(map[int]int)
arrayField[3] = strings.Split(allField[3], ",")
// Check integers
var err error
if intField[2], err = strconv.Atoi(allField[2]); err != nil {
panic(atoiError{_GROUP_FILE, line, "GID"})
}
// Check fields
var isField bool
if G_NAME&_field != 0 && allField[0] == value.(string) {
isField = true
} else if G_PASSWD&_field != 0 && allField[1] == value.(string) {
isField = true
} else if G_GID&_field != 0 && intField[2] == value.(int) {
isField = true
} else if G_MEMBER&_field != 0 && checkGroup(arrayField[3], value.(string)) {
isField = true
} else if G_ALL&_field != 0 {
isField = true
}
if isField {
return &Group{
Name: allField[0],
password: allField[1],
GID: intField[2],
UserList: arrayField[3],
}
}
return nil
}
// LookupGID looks up a group by group ID.
func LookupGID(gid int) (*Group, error) {
entries, err := LookupInGroup(G_GID, gid, 1)
if err != nil {
return nil, err
}
return entries[0], err
}
// LookupGroup looks up a group by name.
func LookupGroup(name string) (*Group, error) {
entries, err := LookupInGroup(G_NAME, name, 1)
if err != nil {
return nil, err
}
return entries[0], err
}
// LookupInGroup looks up a group by the given values.
//
// The count determines the number of fields to return:
// n > 0: at most n fields
// n == 0: the result is nil (zero fields)
// n < 0: all fields
func LookupInGroup(field groupField, value interface{}, n int) ([]*Group, error) {
iEntries, err := lookUp(&Group{}, field, value, n)
if err != nil {
return nil, err
}
// == Convert to type group
valueSlice := reflect.ValueOf(iEntries)
entries := make([]*Group, valueSlice.Len())
for i := 0; i < valueSlice.Len(); i++ {
entries[i] = valueSlice.Index(i).Interface().(*Group)
}
return entries, err
}
// Getgroups returns a list of the numeric ids of groups that the caller
// belongs to.
func Getgroups() []int {
user := GetUsername()
list := make([]int, 0)
// The user could have its own group.
if g, err := LookupGroup(user); err == nil {
list = append(list, g.GID)
}
groups, err := LookupInGroup(G_MEMBER, user, -1)
if err != nil {
if _, ok := err.(NoFoundError); !ok {
panic(err)
}
}
for _, v := range groups {
list = append(list, v.GID)
}
return list
}
// GetgroupsName returns a list of the groups that the caller belongs to.
func GetgroupsName() []string {
user := GetUsername()
list := make([]string, 0)
// The user could have its own group.
if _, err := LookupGroup(user); err == nil {
list = append(list, user)
}
groups, err := LookupInGroup(G_MEMBER, user, -1)
if err != nil {
if _, ok := err.(NoFoundError); !ok {
panic(err)
}
}
for _, v := range groups {
list = append(list, v.Name)
}
return list
}
// == Editing
//
// AddGroup adds a group.
func AddGroup(name string, members ...string) (gid int, err error) {
s := NewGShadow(name, members...)
if err = s.Add(nil); err != nil {
return
}
return NewGroup(name, members...).Add()
}
// AddSystemGroup adds a system group.
func AddSystemGroup(name string, members ...string) (gid int, err error) {
s := NewGShadow(name, members...)
if err = s.Add(nil); err != nil {
return
}
return NewSystemGroup(name, members...).Add()
}
// Add adds a new group.
// Whether GID is < 0, it will choose the first id available in the range set
// in the system configuration.
func (g *Group) Add() (gid int, err error) {
loadConfig()
group, err := LookupGroup(g.Name)
if err != nil {
if _, ok := err.(NoFoundError); !ok {
return 0, err
}
}
if group != nil {
return 0, ErrGroupExist
}
if g.Name == "" {
return 0, RequiredError("Name")
}
var db *dbfile
if g.GID < 0 {
db, gid, err = nextGUID(g.addSystemGroup)
if err != nil {
db.close()
return 0, err
}
g.GID = gid
} else {
db, err = openDBFile(_GROUP_FILE, os.O_WRONLY|os.O_APPEND)
if err != nil {
return
}
// Check if Id is unique.
_, err = LookupGID(g.GID)
if err == nil {
return 0, IdUsedError(g.GID)
} else if _, ok := err.(NoFoundError); !ok {
return 0, err
}
}
g.password = "x"
_, err = db.file.WriteString(g.String())
err2 := db.close()
if err2 != nil && err == nil {
err = err2
}
return
}
// DelGroup removes a group from the system.
func DelGroup(name string) (err error) {
err = del(name, &Group{})
if err == nil {
err = del(name, &GShadow{})
}
return
}
// AddUsersToGroup adds the members to a group.
func AddUsersToGroup(name string, members ...string) error {
if len(members) == 0 {
return fmt.Errorf("no members to add")
}
for i, v := range members {
if v == "" {
return EmptyMemberError(fmt.Sprintf("members[%s]", strconv.Itoa(i)))
}
}
// Group
gr, err := LookupGroup(name)
if err != nil {
return err
}
if err = _addMembers(&gr.UserList, members...); err != nil {
return err
}
// Shadow group
sg, err := LookupGShadow(name)
if err != nil {
return err
}
if err = _addMembers(&sg.UserList, members...); err != nil {
return err
}
// Editing
if err = edit(name, gr); err != nil {
return err
}
if err = edit(name, sg); err != nil {
return err
}
return nil
}
func _addMembers(userList *[]string, members ...string) error {
// Check if some member is already in the file.
for _, u := range *userList {
for _, m := range members {
if u == m {
return fmt.Errorf("user %q is already set", u)
}
}
}
if len(*userList) == 1 && (*userList)[0] == "" {
*userList = members
} else {
*userList = append(*userList, members...)
}
return nil
}
// DelUsersInGroup removes the specific members from a group.
func DelUsersInGroup(name string, members ...string) error {
if len(members) == 0 {
return ErrNoMembers
}
for i, v := range members {
if v == "" {
return EmptyMemberError(fmt.Sprintf("members[%s]", strconv.Itoa(i)))
}
}
// Group
gr, err := LookupGroup(name)
if err != nil {
return err
}
if err = _delMembers(&gr.UserList, members...); err != nil {
return err
}
// Shadow group
sg, err := LookupGShadow(name)
if err != nil {
return err
}
if err = _delMembers(&sg.UserList, members...); err != nil {
return err
}
// Editing
if err = edit(name, gr); err != nil {
return err
}
if err = edit(name, sg); err != nil {
return err
}
return nil
}
func _delMembers(userList *[]string, members ...string) error {
if len(*userList) == 1 && (*userList)[0] == "" {
return ErrNoMembers
}
newUserList := make([]string, 0)
for _, u := range *userList {
found := false
for _, m := range members {
if u == m {
found = true
break
}
}
if !found {
newUserList = append(newUserList, u)
}
}
if len(newUserList) == len(*userList) {
return ErrNoMembers
}
*userList = make([]string, len(newUserList))
for i, v := range newUserList {
(*userList)[i] = v
}
return nil
}
// == Utility
//
// checkGroup indicates if a value is into a group.
func checkGroup(group []string, value string) bool {
for _, v := range group {
if v == value {
return true
}
}
return false
}
// == Errors
//
var ErrNoMembers = errors.New("no members to remove")
// EmptyMemberError reports an empty member.
type EmptyMemberError string
func (e EmptyMemberError) Error() string { return "empty field: " + string(e) }