1// Copyright 2020-2021 InfluxData, Inc. All rights reserved.
2// Use of this source code is governed by MIT
3// license that can be found in the LICENSE file.
4
5package api
6
7import (
8	"context"
9	"encoding/base64"
10	"fmt"
11	nethttp "net/http"
12	"net/http/cookiejar"
13	"sync"
14
15	"github.com/influxdata/influxdb-client-go/v2/api/http"
16	"github.com/influxdata/influxdb-client-go/v2/domain"
17	"golang.org/x/net/publicsuffix"
18)
19
20// UsersAPI provides methods for managing users in a InfluxDB server
21type UsersAPI interface {
22	// GetUsers returns all users
23	GetUsers(ctx context.Context) (*[]domain.User, error)
24	// FindUserByID returns user with userID
25	FindUserByID(ctx context.Context, userID string) (*domain.User, error)
26	// FindUserByName returns user with name userName
27	FindUserByName(ctx context.Context, userName string) (*domain.User, error)
28	// CreateUser creates new user
29	CreateUser(ctx context.Context, user *domain.User) (*domain.User, error)
30	// CreateUserWithName creates new user with userName
31	CreateUserWithName(ctx context.Context, userName string) (*domain.User, error)
32	// UpdateUser updates user
33	UpdateUser(ctx context.Context, user *domain.User) (*domain.User, error)
34	// UpdateUserPassword sets password for an user
35	UpdateUserPassword(ctx context.Context, user *domain.User, password string) error
36	// UpdateUserPasswordWithID sets password for an user with userID
37	UpdateUserPasswordWithID(ctx context.Context, userID string, password string) error
38	// DeleteUserWithID deletes an user with userID
39	DeleteUserWithID(ctx context.Context, userID string) error
40	// DeleteUser deletes an user
41	DeleteUser(ctx context.Context, user *domain.User) error
42	// Me returns actual user
43	Me(ctx context.Context) (*domain.User, error)
44	// MeUpdatePassword set password of actual user
45	MeUpdatePassword(ctx context.Context, oldPassword, newPassword string) error
46	// SignIn exchanges username and password credentials to establish an authenticated session with the InfluxDB server. The Client's authentication token is then ignored, it can be empty.
47	SignIn(ctx context.Context, username, password string) error
48	// SignOut signs out previously signed in user
49	SignOut(ctx context.Context) error
50}
51
52// usersAPI implements UsersAPI
53type usersAPI struct {
54	apiClient       *domain.ClientWithResponses
55	httpService     http.Service
56	httpClient      *nethttp.Client
57	deleteCookieJar bool
58	lock            sync.Mutex
59}
60
61// NewUsersAPI creates new instance of UsersAPI
62func NewUsersAPI(apiClient *domain.ClientWithResponses, httpService http.Service, httpClient *nethttp.Client) UsersAPI {
63	return &usersAPI{
64		apiClient:   apiClient,
65		httpService: httpService,
66		httpClient:  httpClient,
67	}
68}
69
70func (u *usersAPI) GetUsers(ctx context.Context) (*[]domain.User, error) {
71	params := &domain.GetUsersParams{}
72	response, err := u.apiClient.GetUsersWithResponse(ctx, params)
73	if err != nil {
74		return nil, err
75	}
76	if response.JSONDefault != nil {
77		return nil, domain.ErrorToHTTPError(response.JSONDefault, response.StatusCode())
78	}
79	return response.JSON200.Users, nil
80}
81
82func (u *usersAPI) FindUserByID(ctx context.Context, userID string) (*domain.User, error) {
83	params := &domain.GetUsersIDParams{}
84	response, err := u.apiClient.GetUsersIDWithResponse(ctx, userID, params)
85	if err != nil {
86		return nil, err
87	}
88	if response.JSONDefault != nil {
89		return nil, domain.ErrorToHTTPError(response.JSONDefault, response.StatusCode())
90	}
91	return response.JSON200, nil
92}
93
94func (u *usersAPI) FindUserByName(ctx context.Context, userName string) (*domain.User, error) {
95	users, err := u.GetUsers(ctx)
96	if err != nil {
97		return nil, err
98	}
99	var user *domain.User
100	for _, u := range *users {
101		if u.Name == userName {
102			user = &u
103			break
104		}
105	}
106	if user == nil {
107		return nil, fmt.Errorf("user '%s' not found", userName)
108	}
109	return user, nil
110}
111
112func (u *usersAPI) CreateUserWithName(ctx context.Context, userName string) (*domain.User, error) {
113	user := &domain.User{Name: userName}
114	return u.CreateUser(ctx, user)
115}
116
117func (u *usersAPI) CreateUser(ctx context.Context, user *domain.User) (*domain.User, error) {
118	params := &domain.PostUsersParams{}
119	response, err := u.apiClient.PostUsersWithResponse(ctx, params, domain.PostUsersJSONRequestBody(*user))
120	if err != nil {
121		return nil, err
122	}
123	if response.JSONDefault != nil {
124		return nil, domain.ErrorToHTTPError(response.JSONDefault, response.StatusCode())
125	}
126	return response.JSON201, nil
127}
128
129func (u *usersAPI) UpdateUser(ctx context.Context, user *domain.User) (*domain.User, error) {
130	params := &domain.PatchUsersIDParams{}
131	response, err := u.apiClient.PatchUsersIDWithResponse(ctx, *user.Id, params, domain.PatchUsersIDJSONRequestBody(*user))
132	if err != nil {
133		return nil, err
134	}
135	if response.JSONDefault != nil {
136		return nil, domain.ErrorToHTTPError(response.JSONDefault, response.StatusCode())
137	}
138	return response.JSON200, nil
139}
140
141func (u *usersAPI) UpdateUserPassword(ctx context.Context, user *domain.User, password string) error {
142	return u.UpdateUserPasswordWithID(ctx, *user.Id, password)
143}
144
145func (u *usersAPI) UpdateUserPasswordWithID(ctx context.Context, userID string, password string) error {
146	params := &domain.PostUsersIDPasswordParams{}
147	body := &domain.PasswordResetBody{Password: password}
148	response, err := u.apiClient.PostUsersIDPasswordWithResponse(ctx, userID, params, domain.PostUsersIDPasswordJSONRequestBody(*body))
149	if err != nil {
150		return err
151	}
152	if response.JSONDefault != nil {
153		return domain.ErrorToHTTPError(response.JSONDefault, response.StatusCode())
154	}
155	return nil
156}
157
158func (u *usersAPI) DeleteUser(ctx context.Context, user *domain.User) error {
159	return u.DeleteUserWithID(ctx, *user.Id)
160}
161
162func (u *usersAPI) DeleteUserWithID(ctx context.Context, userID string) error {
163	params := &domain.DeleteUsersIDParams{}
164	response, err := u.apiClient.DeleteUsersIDWithResponse(ctx, userID, params)
165	if err != nil {
166		return err
167	}
168	if response.JSONDefault != nil {
169		return domain.ErrorToHTTPError(response.JSONDefault, response.StatusCode())
170	}
171	return nil
172}
173
174func (u *usersAPI) Me(ctx context.Context) (*domain.User, error) {
175	params := &domain.GetMeParams{}
176	response, err := u.apiClient.GetMeWithResponse(ctx, params)
177	if err != nil {
178		return nil, err
179	}
180	if response.JSONDefault != nil {
181		return nil, domain.ErrorToHTTPError(response.JSONDefault, response.StatusCode())
182	}
183	return response.JSON200, nil
184}
185
186func (u *usersAPI) MeUpdatePassword(ctx context.Context, oldPassword, newPassword string) error {
187	u.lock.Lock()
188	defer u.lock.Unlock()
189	me, err := u.Me(ctx)
190	if err != nil {
191		return err
192	}
193	creds := base64.StdEncoding.EncodeToString([]byte(me.Name + ":" + oldPassword))
194	auth := u.httpService.Authorization()
195	defer u.httpService.SetAuthorization(auth)
196	u.httpService.SetAuthorization("Basic " + creds)
197	params := &domain.PutMePasswordParams{}
198	body := &domain.PasswordResetBody{Password: newPassword}
199	response, err := u.apiClient.PutMePasswordWithResponse(ctx, params, domain.PutMePasswordJSONRequestBody(*body))
200	if err != nil {
201		return err
202	}
203	if response.JSONDefault != nil {
204		return domain.ErrorToHTTPError(response.JSONDefault, response.StatusCode())
205	}
206	return nil
207}
208
209func (u *usersAPI) SignIn(ctx context.Context, username, password string) error {
210	u.lock.Lock()
211	defer u.lock.Unlock()
212	if u.httpClient.Jar == nil {
213		jar, err := cookiejar.New(&cookiejar.Options{PublicSuffixList: publicsuffix.List})
214		if err != nil {
215			return err
216		}
217		u.httpClient.Jar = jar
218		u.deleteCookieJar = true
219	}
220	creds := base64.StdEncoding.EncodeToString([]byte(username + ":" + password))
221	u.httpService.SetAuthorization("Basic " + creds)
222	defer u.httpService.SetAuthorization("")
223	resp, err := u.apiClient.PostSigninWithResponse(ctx, &domain.PostSigninParams{})
224	if err != nil {
225		return err
226	}
227	if resp.JSONDefault != nil {
228		return domain.ErrorToHTTPError(resp.JSONDefault, resp.StatusCode())
229	}
230	if resp.JSON401 != nil {
231		return domain.ErrorToHTTPError(resp.JSON401, resp.StatusCode())
232	}
233	if resp.JSON403 != nil {
234		return domain.ErrorToHTTPError(resp.JSON403, resp.StatusCode())
235	}
236	return nil
237}
238
239func (u *usersAPI) SignOut(ctx context.Context) error {
240	u.lock.Lock()
241	defer u.lock.Unlock()
242	resp, err := u.apiClient.PostSignoutWithResponse(ctx, &domain.PostSignoutParams{})
243	if err != nil {
244		return err
245	}
246	if resp.JSONDefault != nil {
247		return domain.ErrorToHTTPError(resp.JSONDefault, resp.StatusCode())
248	}
249	if resp.JSON401 != nil {
250		return domain.ErrorToHTTPError(resp.JSON401, resp.StatusCode())
251	}
252	if u.deleteCookieJar {
253		u.httpClient.Jar = nil
254	}
255	return nil
256}
257