Allow making LDAP users admins

This commit is contained in:
Lou Knauer 2022-04-11 12:29:24 +02:00
parent 6e9a916a18
commit e0cc17cfa9
3 changed files with 88 additions and 6 deletions

View File

@ -55,6 +55,7 @@ func (api *RestApi) MountRoutes(r *mux.Router) {
r.HandleFunc("/users/", api.createUser).Methods(http.MethodPost, http.MethodPut)
r.HandleFunc("/users/", api.getUsers).Methods(http.MethodGet)
r.HandleFunc("/users/", api.deleteUser).Methods(http.MethodDelete)
r.HandleFunc("/user/{id}", api.updateUser).Methods(http.MethodPost)
r.HandleFunc("/configuration/", api.updateConfiguration).Methods(http.MethodPost)
}
@ -555,7 +556,9 @@ func (api *RestApi) getUsers(rw http.ResponseWriter, r *http.Request) {
return
}
users, err := api.Authentication.FetchUsers(r.URL.Query().Get("via-ldap") == "true")
users, err := api.Authentication.FetchUsers(
r.URL.Query().Get("via-ldap") == "true",
r.URL.Query().Get("not-just-user") == "true")
if err != nil {
http.Error(rw, err.Error(), http.StatusInternalServerError)
return
@ -564,6 +567,22 @@ func (api *RestApi) getUsers(rw http.ResponseWriter, r *http.Request) {
json.NewEncoder(rw).Encode(users)
}
func (api *RestApi) updateUser(rw http.ResponseWriter, r *http.Request) {
if user := auth.GetUser(r.Context()); !user.HasRole(auth.RoleAdmin) {
http.Error(rw, "only admins are allowed to update a user", http.StatusForbidden)
return
}
// TODO: Handle anything but roles...
newrole := r.FormValue("add-role")
if err := api.Authentication.AddRole(r.Context(), mux.Vars(r)["id"], newrole); err != nil {
http.Error(rw, err.Error(), http.StatusUnprocessableEntity)
return
}
rw.Write([]byte("success"))
}
func (api *RestApi) updateConfiguration(rw http.ResponseWriter, r *http.Request) {
rw.Header().Set("Content-Type", "text/plain")
key, value := r.FormValue("key"), r.FormValue("value")

View File

@ -174,18 +174,49 @@ func (auth *Authentication) CreateUser(username, name, password, email string, r
return nil
}
func (auth *Authentication) AddRole(ctx context.Context, username string, role string) error {
user, err := auth.FetchUser(username)
if err != nil {
return err
}
if role != RoleAdmin && role != RoleApi && role != RoleUser {
return fmt.Errorf("invalid user role: %#v", role)
}
for _, r := range user.Roles {
if r == role {
return fmt.Errorf("user %#v already has role %#v", username, role)
}
}
roles, _ := json.Marshal(append(user.Roles, role))
if _, err := sq.Update("user").Set("roles", roles).Where("user.username = ?", username).RunWith(auth.db).Exec(); err != nil {
return err
}
return nil
}
func (auth *Authentication) DelUser(username string) error {
_, err := auth.db.Exec(`DELETE FROM user WHERE user.username = ?`, username)
return err
}
func (auth *Authentication) FetchUsers(viaLdap bool) ([]*User, error) {
func (auth *Authentication) FetchUsers(viaLdap, notJustUser bool) ([]*User, error) {
q := sq.Select("username", "name", "email", "roles").From("user")
if !viaLdap {
if notJustUser {
q = q.Where("ldap = 0 OR roles != '[\"user\"]'")
} else {
q = q.Where("ldap = 0")
}
} else {
if notJustUser {
q = q.Where("ldap = 1 OR roles != '[\"user\"]'")
} else {
q = q.Where("ldap = 1")
}
}
rows, err := q.RunWith(auth.db).Query()
if err != nil {

View File

@ -47,7 +47,8 @@
</div>
<div class="col card" style="margin: 10px;">
<div class="card-body">
<h5 class="card-title">All Users not Created by an LDAP Sync:</h5>
<h5 class="card-title">Special Users</h5>
<p>Not created by an LDAP sync and/or having a role other than <code>user</code></p>
<table class="table">
<thead>
<tr>
@ -61,7 +62,7 @@
</thead>
<tbody id="users-list"><tr><td colspan="4"><div class="spinner-border" role="status"><span class="visually-hidden">Loading...</span></div></td></tr></tbody>
<script>
fetch('/api/users/?via-ldap=false')
fetch('/api/users/?via-ldap=false&not-just-user=true')
.then(res => res.json())
.then(users => {
let listElement = document.querySelector('#users-list')
@ -105,6 +106,37 @@
</div>
</div>
</div>
<div class="row">
<div class="col card" style="margin: 10px;">
<div class="card-body">
<h5 class="card-title">Add Role to User</h5>
<div class="input-group">
<input type="text" class="form-control" placeholder="username" id="add-role-username"/>
<select class="form-select" id="add-role-select">
<option selected value="">Role...</option>
<option value="user">User</option>
<option value="admin">Admin</option>
<option value="api">API</option>
</select>
<button class="btn btn-outline-secondary" type="button" id="add-role-button">Button</button>
</div>
<script>
document.querySelector('#add-role-button').addEventListener('click', () => {
const username = document.querySelector('#add-role-username').value, role = document.querySelector('#add-role-select').value
if (username == "" || role == "") {
alert('Please fill in a username and select a role.')
return
}
let formData = new FormData()
formData.append('username', username)
formData.append('add-role', role)
fetch(`/api/user/${username}`, { method: 'POST', body: formData }).then(res => res.text()).then(status => alert(status))
})
</script>
</div>
</div>
</div>
{{end}}
<div class="row">
<div class="col card" style="margin: 10px;">