1package api
2
3import (
4	"database/sql"
5	"net/http"
6	"time"
7
8	log "github.com/Sirupsen/logrus"
9	"github.com/ansible-semaphore/semaphore/db"
10
11	"github.com/ansible-semaphore/semaphore/util"
12	"github.com/gorilla/context"
13	"golang.org/x/crypto/bcrypt"
14)
15
16func getUsers(w http.ResponseWriter, r *http.Request) {
17	var users []db.User
18	if _, err := db.Mysql.Select(&users, "select * from user"); err != nil {
19		panic(err)
20	}
21
22	util.WriteJSON(w, http.StatusOK, users)
23}
24
25func addUser(w http.ResponseWriter, r *http.Request) {
26	type User struct {
27		Pwd string    `db:"-" json:"password"`
28		db.User
29	}
30
31	var user User
32	if err := util.Bind(w, r, &user); err != nil {
33		w.WriteHeader(http.StatusBadRequest)
34		return
35	}
36
37	editor := context.Get(r, "user").(*db.User)
38	if !editor.Admin {
39		log.Warn(editor.Username + " is not permitted to create users")
40		w.WriteHeader(http.StatusUnauthorized)
41		return
42	}
43
44	password, err := bcrypt.GenerateFromPassword([]byte(user.Pwd), 11)
45	if err != nil {
46		w.WriteHeader(http.StatusBadRequest)
47		return
48	}
49
50	user.Password = string(password)
51	user.Created = db.GetParsedTime(time.Now())
52
53	err = db.Mysql.Insert(&user.User)
54
55	if err != nil {
56		log.Warn(editor.Username + " is not created: " + err.Error())
57		w.WriteHeader(http.StatusBadRequest)
58	}
59
60	util.WriteJSON(w, http.StatusCreated, user)
61}
62
63func getUserMiddleware(next http.Handler) http.Handler {
64	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
65		userID, err := util.GetIntParam("user_id", w, r)
66		if err != nil {
67			return
68		}
69
70		var user db.User
71		if err := db.Mysql.SelectOne(&user, "select * from user where id=?", userID); err != nil {
72			if err == sql.ErrNoRows {
73				w.WriteHeader(http.StatusNotFound)
74				return
75			}
76
77			panic(err)
78		}
79
80		editor := context.Get(r, "user").(*db.User)
81		if !editor.Admin && editor.ID != user.ID {
82			log.Warn(editor.Username + " is not permitted to edit users")
83			w.WriteHeader(http.StatusUnauthorized)
84			return
85		}
86
87		context.Set(r, "_user", user)
88		next.ServeHTTP(w, r)
89	})
90}
91
92func updateUser(w http.ResponseWriter, r *http.Request) {
93	oldUser := context.Get(r, "_user").(db.User)
94	editor := context.Get(r, "user").(*db.User)
95
96	type User struct {
97		Pwd string    `db:"password" json:"password"`
98		db.User
99	}
100
101	var user User
102	if err := util.Bind(w, r, &user); err != nil {
103		return
104	}
105
106	if !editor.Admin && editor.ID != oldUser.ID {
107		log.Warn(editor.Username + " is not permitted to edit users")
108		w.WriteHeader(http.StatusUnauthorized)
109		return
110	}
111
112	if editor.ID == oldUser.ID && oldUser.Admin != user.Admin {
113		log.Warn("User can't edit his own role")
114		w.WriteHeader(http.StatusUnauthorized)
115		return
116	}
117
118	if oldUser.External && oldUser.Username != user.Username {
119		log.Warn("Username is not editable for external LDAP users")
120		w.WriteHeader(http.StatusBadRequest)
121		return
122	}
123
124	var err error
125
126	if user.Pwd != "" {
127		var password []byte
128		password, err = bcrypt.GenerateFromPassword([]byte(user.Pwd), 11)
129		if err != nil {
130			w.WriteHeader(http.StatusBadRequest)
131			return
132		}
133		_, err = db.Mysql.Exec(
134			"update user set name=?, username=?, email=?, alert=?, admin=?, password=? where id=?",
135			user.Name,
136			user.Username,
137			user.Email,
138			user.Alert,
139			user.Admin,
140			string(password),
141			oldUser.ID)
142
143	} else {
144		_, err = db.Mysql.Exec(
145			"update user set name=?, username=?, email=?, alert=?, admin=? where id=?",
146			user.Name,
147			user.Username,
148			user.Email,
149			user.Alert,
150			user.Admin,
151			oldUser.ID)
152	}
153
154	if err != nil {
155		log.Error(err.Error())
156		w.WriteHeader(http.StatusBadRequest)
157		return
158	}
159
160	w.WriteHeader(http.StatusNoContent)
161}
162
163func updateUserPassword(w http.ResponseWriter, r *http.Request) {
164	user := context.Get(r, "_user").(db.User)
165	editor := context.Get(r, "user").(*db.User)
166
167	var pwd struct {
168		Pwd string `json:"password"`
169	}
170
171	if !editor.Admin && editor.ID != user.ID {
172		log.Warn(editor.Username + " is not permitted to edit users")
173		w.WriteHeader(http.StatusUnauthorized)
174		return
175	}
176
177	if user.External {
178		log.Warn("Password is not editable for external LDAP users")
179		w.WriteHeader(http.StatusBadRequest)
180		return
181	}
182
183	if err := util.Bind(w, r, &pwd); err != nil {
184		return
185	}
186
187	password, err := bcrypt.GenerateFromPassword([]byte(pwd.Pwd), 11)
188	util.LogWarning(err)
189	if _, err := db.Mysql.Exec("update user set password=? where id=?", string(password), user.ID); err != nil {
190		panic(err)
191	}
192
193	w.WriteHeader(http.StatusNoContent)
194}
195
196func deleteUser(w http.ResponseWriter, r *http.Request) {
197	user := context.Get(r, "_user").(db.User)
198	editor := context.Get(r, "user").(*db.User)
199
200	if !editor.Admin && editor.ID != user.ID {
201		log.Warn(editor.Username + " is not permitted to delete users")
202		w.WriteHeader(http.StatusUnauthorized)
203		return
204	}
205
206	if _, err := db.Mysql.Exec("delete from project__user where user_id=?", user.ID); err != nil {
207		panic(err)
208	}
209	if _, err := db.Mysql.Exec("delete from user where id=?", user.ID); err != nil {
210		panic(err)
211	}
212
213	w.WriteHeader(http.StatusNoContent)
214}
215