github.com/Lephar/snapd@v0.0.0-20210825215435-c7fba9cef4d2/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  	// Automatic is for internal snapd use, behavior might evolve
    49  	Automatic bool `json:"automatic,omitempty"`
    50  }
    51  
    52  // RemoveUserOptions holds options for removing a local system user.
    53  type RemoveUserOptions struct {
    54  	// Username indicates which user to remove.
    55  	Username string `json:"username,omitempty"`
    56  }
    57  
    58  type userAction struct {
    59  	Action string `json:"action"`
    60  	*CreateUserOptions
    61  	*RemoveUserOptions
    62  }
    63  
    64  func (client *Client) doUserAction(act *userAction, result interface{}) error {
    65  	data, err := json.Marshal(act)
    66  	if err != nil {
    67  		return err
    68  	}
    69  
    70  	_, err = client.doSync("POST", "/v2/users", nil, nil, bytes.NewReader(data), result)
    71  	return err
    72  }
    73  
    74  // CreateUser creates a local system user. See CreateUserOptions for details.
    75  func (client *Client) CreateUser(options *CreateUserOptions) (*CreateUserResult, error) {
    76  	if options == nil || options.Email == "" {
    77  		return nil, fmt.Errorf("cannot create a user without providing an email")
    78  	}
    79  
    80  	var result []*CreateUserResult
    81  	err := client.doUserAction(&userAction{Action: "create", CreateUserOptions: options}, &result)
    82  	if err != nil {
    83  		return nil, fmt.Errorf("while creating user: %v", err)
    84  	}
    85  	return result[0], nil
    86  }
    87  
    88  // CreateUsers creates multiple local system users. See CreateUserOptions for details.
    89  //
    90  // Results may be provided even if there are errors.
    91  func (client *Client) CreateUsers(options []*CreateUserOptions) ([]*CreateUserResult, error) {
    92  	for _, opts := range options {
    93  		if opts == nil || (opts.Email == "" && !(opts.Known || opts.Automatic)) {
    94  			return nil, fmt.Errorf("cannot create user from store details without an email to query for")
    95  		}
    96  	}
    97  
    98  	var results []*CreateUserResult
    99  	var errs []error
   100  	for _, opts := range options {
   101  		var result []*CreateUserResult
   102  		err := client.doUserAction(&userAction{Action: "create", CreateUserOptions: opts}, &result)
   103  		if err != nil {
   104  			errs = append(errs, err)
   105  		} else {
   106  			results = append(results, result...)
   107  		}
   108  	}
   109  
   110  	if len(errs) == 1 {
   111  		return results, errs[0]
   112  	}
   113  	if len(errs) > 1 {
   114  		var buf bytes.Buffer
   115  		for _, err := range errs {
   116  			fmt.Fprintf(&buf, "\n- %s", err)
   117  		}
   118  		return results, fmt.Errorf("while creating users:%s", buf.Bytes())
   119  	}
   120  	return results, nil
   121  }
   122  
   123  // RemoveUser removes a local system user.
   124  func (client *Client) RemoveUser(options *RemoveUserOptions) (removed []*User, err error) {
   125  	if options == nil || options.Username == "" {
   126  		return nil, fmt.Errorf("cannot remove a user without providing a username")
   127  	}
   128  	var result struct {
   129  		Removed []*User `json:"removed"`
   130  	}
   131  	if err := client.doUserAction(&userAction{Action: "remove", RemoveUserOptions: options}, &result); err != nil {
   132  		return nil, err
   133  	}
   134  	return result.Removed, nil
   135  }
   136  
   137  // Users returns the local users.
   138  func (client *Client) Users() ([]*User, error) {
   139  	var result []*User
   140  
   141  	if _, err := client.doSync("GET", "/v2/users", nil, nil, nil, &result); err != nil {
   142  		return nil, fmt.Errorf("while getting users: %v", err)
   143  	}
   144  	return result, nil
   145  }