1package cfclient
2
3import (
4	"bytes"
5	"encoding/json"
6	"fmt"
7	"io/ioutil"
8	"net/http"
9	"net/url"
10
11	"github.com/pkg/errors"
12)
13
14type UserRequest struct {
15	Guid             string `json:"guid"`
16	DefaultSpaceGuid string `json:"default_space_guid,omitempty"`
17}
18
19type Users []User
20
21type User struct {
22	Guid                  string `json:"guid"`
23	CreatedAt             string `json:"created_at"`
24	UpdatedAt             string `json:"updated_at"`
25	Admin                 bool   `json:"admin"`
26	Active                bool   `json:"active"`
27	DefaultSpaceGUID      string `json:"default_space_guid"`
28	Username              string `json:"username"`
29	SpacesURL             string `json:"spaces_url"`
30	OrgsURL               string `json:"organizations_url"`
31	ManagedOrgsURL        string `json:"managed_organizations_url"`
32	BillingManagedOrgsURL string `json:"billing_managed_organizations_url"`
33	AuditedOrgsURL        string `json:"audited_organizations_url"`
34	ManagedSpacesURL      string `json:"managed_spaces_url"`
35	AuditedSpacesURL      string `json:"audited_spaces_url"`
36	c                     *Client
37}
38
39type UserResource struct {
40	Meta   Meta `json:"metadata"`
41	Entity User `json:"entity"`
42}
43
44type UserResponse struct {
45	Count     int            `json:"total_results"`
46	Pages     int            `json:"total_pages"`
47	NextUrl   string         `json:"next_url"`
48	Resources []UserResource `json:"resources"`
49}
50
51// GetUserByGUID retrieves the user with the provided guid.
52func (c *Client) GetUserByGUID(guid string) (User, error) {
53	var userRes UserResource
54	r := c.NewRequest("GET", "/v2/users/"+guid)
55	resp, err := c.DoRequest(r)
56	if err != nil {
57		return User{}, err
58	}
59	body, err := ioutil.ReadAll(resp.Body)
60	defer resp.Body.Close()
61	if err != nil {
62		return User{}, err
63	}
64	err = json.Unmarshal(body, &userRes)
65	if err != nil {
66		return User{}, err
67	}
68	return c.mergeUserResource(userRes), nil
69}
70
71func (c *Client) ListUsersByQuery(query url.Values) (Users, error) {
72	var users []User
73	requestUrl := "/v2/users?" + query.Encode()
74	for {
75		userResp, err := c.getUserResponse(requestUrl)
76		if err != nil {
77			return []User{}, err
78		}
79		for _, user := range userResp.Resources {
80			user.Entity.Guid = user.Meta.Guid
81			user.Entity.CreatedAt = user.Meta.CreatedAt
82			user.Entity.UpdatedAt = user.Meta.UpdatedAt
83			user.Entity.c = c
84			users = append(users, user.Entity)
85		}
86		requestUrl = userResp.NextUrl
87		if requestUrl == "" {
88			break
89		}
90	}
91	return users, nil
92}
93
94func (c *Client) ListUsers() (Users, error) {
95	return c.ListUsersByQuery(nil)
96}
97
98func (c *Client) ListUserSpaces(userGuid string) ([]Space, error) {
99	return c.fetchSpaces(fmt.Sprintf("/v2/users/%s/spaces", userGuid))
100}
101
102func (c *Client) ListUserAuditedSpaces(userGuid string) ([]Space, error) {
103	return c.fetchSpaces(fmt.Sprintf("/v2/users/%s/audited_spaces", userGuid))
104}
105
106func (c *Client) ListUserManagedSpaces(userGuid string) ([]Space, error) {
107	return c.fetchSpaces(fmt.Sprintf("/v2/users/%s/managed_spaces", userGuid))
108}
109
110func (c *Client) ListUserOrgs(userGuid string) ([]Org, error) {
111	return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/organizations", userGuid))
112}
113
114func (c *Client) ListUserManagedOrgs(userGuid string) ([]Org, error) {
115	return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/managed_organizations", userGuid))
116}
117
118func (c *Client) ListUserAuditedOrgs(userGuid string) ([]Org, error) {
119	return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/audited_organizations", userGuid))
120}
121
122func (c *Client) ListUserBillingManagedOrgs(userGuid string) ([]Org, error) {
123	return c.fetchOrgs(fmt.Sprintf("/v2/users/%s/billing_managed_organizations", userGuid))
124}
125
126func (c *Client) CreateUser(req UserRequest) (User, error) {
127	buf := bytes.NewBuffer(nil)
128	err := json.NewEncoder(buf).Encode(req)
129	if err != nil {
130		return User{}, err
131	}
132	r := c.NewRequestWithBody("POST", "/v2/users", buf)
133	resp, err := c.DoRequest(r)
134	if err != nil {
135		return User{}, err
136	}
137	if resp.StatusCode != http.StatusCreated {
138		return User{}, errors.Wrapf(err, "Error creating user, response code: %d", resp.StatusCode)
139	}
140	body, err := ioutil.ReadAll(resp.Body)
141	defer resp.Body.Close()
142	if err != nil {
143		return User{}, err
144	}
145	var userResource UserResource
146	err = json.Unmarshal(body, &userResource)
147	if err != nil {
148		return User{}, err
149	}
150	user := userResource.Entity
151	user.Guid = userResource.Meta.Guid
152	user.c = c
153	return user, nil
154}
155
156func (c *Client) DeleteUser(userGuid string) error {
157	resp, err := c.DoRequest(c.NewRequest("DELETE", fmt.Sprintf("/v2/users/%s", userGuid)))
158	if err != nil {
159		return err
160	}
161	if resp.StatusCode != http.StatusNoContent {
162		return errors.Wrapf(err, "Error deleting user %s, response code: %d", userGuid, resp.StatusCode)
163	}
164	return nil
165}
166
167func (u Users) GetUserByUsername(username string) User {
168	for _, user := range u {
169		if user.Username == username {
170			return user
171		}
172	}
173	return User{}
174}
175
176func (c *Client) getUserResponse(requestUrl string) (UserResponse, error) {
177	var userResp UserResponse
178	r := c.NewRequest("GET", requestUrl)
179	resp, err := c.DoRequest(r)
180	if err != nil {
181		return UserResponse{}, errors.Wrap(err, "Error requesting users")
182	}
183	resBody, err := ioutil.ReadAll(resp.Body)
184	defer resp.Body.Close()
185	if err != nil {
186		return UserResponse{}, errors.Wrap(err, "Error reading user request")
187	}
188	err = json.Unmarshal(resBody, &userResp)
189	if err != nil {
190		return UserResponse{}, errors.Wrap(err, "Error unmarshalling user")
191	}
192	return userResp, nil
193}
194
195func (c *Client) mergeUserResource(u UserResource) User {
196	u.Entity.Guid = u.Meta.Guid
197	u.Entity.CreatedAt = u.Meta.CreatedAt
198	u.Entity.UpdatedAt = u.Meta.UpdatedAt
199	u.Entity.c = c
200	return u.Entity
201}
202