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  }