github.com/kubiko/snapd@v0.0.0-20201013125620-d4f3094d9ddf/client/users.go (about) 1 // -*- Mode: Go; indent-tabs-mode: t -*- 2 3 /* 4 * Copyright (C) 2015-2020 Canonical Ltd 5 * 6 * This program is free software: you can redistribute it and/or modify 7 * it under the terms of the GNU General Public License version 3 as 8 * published by the Free Software Foundation. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * 15 * You should have received a copy of the GNU General Public License 16 * along with this program. If not, see <http://www.gnu.org/licenses/>. 17 * 18 */ 19 20 package client 21 22 import ( 23 "bytes" 24 "encoding/json" 25 "fmt" 26 ) 27 28 // CreateUserResult holds the result of a user creation. 29 type CreateUserResult struct { 30 Username string `json:"username"` 31 SSHKeys []string `json:"ssh-keys"` 32 } 33 34 // CreateUserOptions holds options for creating a local system user. 35 // 36 // If Known is false, the provided email is used to query the store for 37 // username and SSH key details. 38 // 39 // If Known is true, the user will be created by looking through existing 40 // system-user assertions and looking for a matching email. If Email is 41 // empty then all such assertions are considered and multiple users may 42 // be created. 43 type CreateUserOptions struct { 44 Email string `json:"email,omitempty"` 45 Sudoer bool `json:"sudoer,omitempty"` 46 Known bool `json:"known,omitempty"` 47 ForceManaged bool `json:"force-managed,omitempty"` 48 } 49 50 // RemoveUserOptions holds options for removing a local system user. 51 type RemoveUserOptions struct { 52 // Username indicates which user to remove. 53 Username string `json:"username,omitempty"` 54 } 55 56 type userAction struct { 57 Action string `json:"action"` 58 *CreateUserOptions 59 *RemoveUserOptions 60 } 61 62 func (client *Client) doUserAction(act *userAction, result interface{}) error { 63 data, err := json.Marshal(act) 64 if err != nil { 65 return err 66 } 67 68 _, err = client.doSync("POST", "/v2/users", nil, nil, bytes.NewReader(data), result) 69 return err 70 } 71 72 // CreateUser creates a local system user. See CreateUserOptions for details. 73 func (client *Client) CreateUser(options *CreateUserOptions) (*CreateUserResult, error) { 74 if options == nil || options.Email == "" { 75 return nil, fmt.Errorf("cannot create a user without providing an email") 76 } 77 78 var result []*CreateUserResult 79 err := client.doUserAction(&userAction{Action: "create", CreateUserOptions: options}, &result) 80 if err != nil { 81 return nil, fmt.Errorf("while creating user: %v", err) 82 } 83 return result[0], nil 84 } 85 86 // CreateUsers creates multiple local system users. See CreateUserOptions for details. 87 // 88 // Results may be provided even if there are errors. 89 func (client *Client) CreateUsers(options []*CreateUserOptions) ([]*CreateUserResult, error) { 90 for _, opts := range options { 91 if opts == nil || (opts.Email == "" && !opts.Known) { 92 return nil, fmt.Errorf("cannot create user from store details without an email to query for") 93 } 94 } 95 96 var results []*CreateUserResult 97 var errs []error 98 for _, opts := range options { 99 var result []*CreateUserResult 100 err := client.doUserAction(&userAction{Action: "create", CreateUserOptions: opts}, &result) 101 if err != nil { 102 errs = append(errs, err) 103 } else { 104 results = append(results, result...) 105 } 106 } 107 108 if len(errs) == 1 { 109 return results, errs[0] 110 } 111 if len(errs) > 1 { 112 var buf bytes.Buffer 113 for _, err := range errs { 114 fmt.Fprintf(&buf, "\n- %s", err) 115 } 116 return results, fmt.Errorf("while creating users:%s", buf.Bytes()) 117 } 118 return results, nil 119 } 120 121 // RemoveUser removes a local system user. 122 func (client *Client) RemoveUser(options *RemoveUserOptions) (removed []*User, err error) { 123 if options == nil || options.Username == "" { 124 return nil, fmt.Errorf("cannot remove a user without providing a username") 125 } 126 var result struct { 127 Removed []*User `json:"removed"` 128 } 129 if err := client.doUserAction(&userAction{Action: "remove", RemoveUserOptions: options}, &result); err != nil { 130 return nil, err 131 } 132 return result.Removed, nil 133 } 134 135 // Users returns the local users. 136 func (client *Client) Users() ([]*User, error) { 137 var result []*User 138 139 if _, err := client.doSync("GET", "/v2/users", nil, nil, nil, &result); err != nil { 140 return nil, fmt.Errorf("while getting users: %v", err) 141 } 142 return result, nil 143 }