mirror of
https://github.com/jech/galene.git
synced 2024-11-22 00:25:58 +01:00
Add HTTP API for wildcard and empty users.
This commit is contained in:
parent
d29d14da16
commit
ef6cff1d24
3 changed files with 63 additions and 18 deletions
12
README.API
12
README.API
|
@ -70,17 +70,23 @@ accepted content-type is `application/jwk-set+json`.
|
||||||
Returns a list of users, as a JSON array. The only allowed methods are
|
Returns a list of users, as a JSON array. The only allowed methods are
|
||||||
HEAD and GET.
|
HEAD and GET.
|
||||||
|
|
||||||
### User definition
|
### User definitions
|
||||||
|
|
||||||
/galene-api/v0/.groups/groupname/.users/username
|
/galene-api/v0/.groups/groupname/.users/username
|
||||||
|
/galene-api/v0/.groups/groupname/.empty-user
|
||||||
|
/galene-api/v0/.groups/groupname/.wildcard-user
|
||||||
|
|
||||||
Contains a "sanitised" user definition (without any passwords), a JSON
|
Contains a "sanitised" user definition (without any passwords), a JSON
|
||||||
object with a single field `permissions`. Allowed methods are HEAD, GET,
|
object with a single field `permissions`. The entries `.empty-user` and
|
||||||
PUT and DELETE. The only accepted content-type is `application/json`.
|
`.wildcard-user` are for the user with the empty username and the wildcard
|
||||||
|
user respectively. Allowed methods are HEAD, GET, PUT and DELETE. The
|
||||||
|
only accepted content-type is `application/json`.
|
||||||
|
|
||||||
### Passwords
|
### Passwords
|
||||||
|
|
||||||
/galene-api/v0/.groups/groupname/.users/username/.password
|
/galene-api/v0/.groups/groupname/.users/username/.password
|
||||||
|
/galene-api/v0/.groups/groupname/.empty-user/.password
|
||||||
|
/galene-api/v0/.groups/groupname/.wildcard-user/.password
|
||||||
|
|
||||||
Contains the password of a given user. The PUT method takes a full
|
Contains the password of a given user. The PUT method takes a full
|
||||||
password definition, identical to what can appear in the `"password"`
|
password definition, identical to what can appear in the `"password"`
|
||||||
|
|
|
@ -39,7 +39,7 @@ func checkAdmin(w http.ResponseWriter, r *http.Request) bool {
|
||||||
// checkPasswordAdmin checks whether the client authentifies as either an
|
// checkPasswordAdmin checks whether the client authentifies as either an
|
||||||
// administrator or the given user. It is used to check whether the
|
// administrator or the given user. It is used to check whether the
|
||||||
// client has the right to change user's password.
|
// client has the right to change user's password.
|
||||||
func checkPasswordAdmin(w http.ResponseWriter, r *http.Request, groupname, user string) bool {
|
func checkPasswordAdmin(w http.ResponseWriter, r *http.Request, groupname, user string, wildcard bool) bool {
|
||||||
username, password, ok := r.BasicAuth()
|
username, password, ok := r.BasicAuth()
|
||||||
if ok {
|
if ok {
|
||||||
ok, _ := adminMatch(username, password)
|
ok, _ := adminMatch(username, password)
|
||||||
|
@ -47,7 +47,7 @@ func checkPasswordAdmin(w http.ResponseWriter, r *http.Request, groupname, user
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if ok && username == user {
|
if ok && !wildcard && username == user {
|
||||||
desc, err := group.GetDescription(groupname)
|
desc, err := group.GetDescription(groupname)
|
||||||
if err == nil && desc.Users != nil {
|
if err == nil && desc.Users != nil {
|
||||||
u, ok := desc.Users[user]
|
u, ok := desc.Users[user]
|
||||||
|
@ -161,6 +161,12 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
|
||||||
if kind == ".users" {
|
if kind == ".users" {
|
||||||
usersHandler(w, r, g, rest)
|
usersHandler(w, r, g, rest)
|
||||||
return
|
return
|
||||||
|
} else if kind == ".empty-user" {
|
||||||
|
specialUserHandler(w, r, g, rest, false)
|
||||||
|
return
|
||||||
|
} else if kind == ".wildcard-user" {
|
||||||
|
specialUserHandler(w, r, g, rest, true)
|
||||||
|
return
|
||||||
} else if kind == ".keys" && rest == "" {
|
} else if kind == ".keys" && rest == "" {
|
||||||
keysHandler(w, r, g)
|
keysHandler(w, r, g)
|
||||||
return
|
return
|
||||||
|
@ -278,10 +284,10 @@ func usersHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
|
||||||
|
|
||||||
first2, kind2, rest2 := splitPath(pth)
|
first2, kind2, rest2 := splitPath(pth)
|
||||||
if first2 != "" && kind2 == "" {
|
if first2 != "" && kind2 == "" {
|
||||||
userHandler(w, r, g, first2[1:])
|
userHandler(w, r, g, first2[1:], false)
|
||||||
return
|
return
|
||||||
} else if first2 != "" && kind2 == ".password" && rest2 == "" {
|
} else if first2 != "" && kind2 == ".password" && rest2 == "" {
|
||||||
passwordHandler(w, r, g, first2[1:])
|
passwordHandler(w, r, g, first2[1:], false)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if !checkAdmin(w, r) {
|
if !checkAdmin(w, r) {
|
||||||
|
@ -291,13 +297,28 @@ func usersHandler(w http.ResponseWriter, r *http.Request, g, pth string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func userHandler(w http.ResponseWriter, r *http.Request, g, user string) {
|
func specialUserHandler(w http.ResponseWriter, r *http.Request, g, pth string, wildcard bool) {
|
||||||
|
if pth == "" {
|
||||||
|
userHandler(w, r, g, "", wildcard)
|
||||||
|
return
|
||||||
|
} else if pth == ".password" {
|
||||||
|
passwordHandler(w, r, g, "", wildcard)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if !checkAdmin(w, r) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
notFound(w)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
func userHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcard bool) {
|
||||||
if !checkAdmin(w, r) {
|
if !checkAdmin(w, r) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if r.Method == "HEAD" || r.Method == "GET" {
|
if r.Method == "HEAD" || r.Method == "GET" {
|
||||||
user, etag, err := group.GetSanitisedUser(g, user, false)
|
user, etag, err := group.GetSanitisedUser(g, user, wildcard)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
|
@ -310,7 +331,7 @@ func userHandler(w http.ResponseWriter, r *http.Request, g, user string) {
|
||||||
sendJSON(w, r, user)
|
sendJSON(w, r, user)
|
||||||
return
|
return
|
||||||
} else if r.Method == "PUT" {
|
} else if r.Method == "PUT" {
|
||||||
etag, err := group.GetUserTag(g, user, false)
|
etag, err := group.GetUserTag(g, user, wildcard)
|
||||||
if errors.Is(err, os.ErrNotExist) {
|
if errors.Is(err, os.ErrNotExist) {
|
||||||
etag = ""
|
etag = ""
|
||||||
err = nil
|
err = nil
|
||||||
|
@ -329,7 +350,7 @@ func userHandler(w http.ResponseWriter, r *http.Request, g, user string) {
|
||||||
if done {
|
if done {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err = group.UpdateUser(g, user, false, etag, &newdesc)
|
err = group.UpdateUser(g, user, wildcard, etag, &newdesc)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
|
@ -341,7 +362,7 @@ func userHandler(w http.ResponseWriter, r *http.Request, g, user string) {
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
} else if r.Method == "DELETE" {
|
} else if r.Method == "DELETE" {
|
||||||
etag, err := group.GetUserTag(g, user, false)
|
etag, err := group.GetUserTag(g, user, wildcard)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
|
@ -352,7 +373,7 @@ func userHandler(w http.ResponseWriter, r *http.Request, g, user string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
err = group.DeleteUser(g, user, false, etag)
|
err = group.DeleteUser(g, user, wildcard, etag)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
|
@ -364,8 +385,8 @@ func userHandler(w http.ResponseWriter, r *http.Request, g, user string) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string) {
|
func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string, wildcard bool) {
|
||||||
if !checkPasswordAdmin(w, r, g, user) {
|
if !checkPasswordAdmin(w, r, g, user, wildcard) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -375,7 +396,7 @@ func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string) {
|
||||||
if done {
|
if done {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
err := group.SetUserPassword(g, user, false, pw)
|
err := group.SetUserPassword(g, user, wildcard, pw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
|
@ -403,7 +424,7 @@ func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string) {
|
||||||
Salt: hex.EncodeToString(salt),
|
Salt: hex.EncodeToString(salt),
|
||||||
Iterations: iterations,
|
Iterations: iterations,
|
||||||
}
|
}
|
||||||
err = group.SetUserPassword(g, user, false, pw)
|
err = group.SetUserPassword(g, user, wildcard, pw)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
|
@ -411,7 +432,7 @@ func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string) {
|
||||||
w.WriteHeader(http.StatusNoContent)
|
w.WriteHeader(http.StatusNoContent)
|
||||||
return
|
return
|
||||||
} else if r.Method == "DELETE" {
|
} else if r.Method == "DELETE" {
|
||||||
err := group.SetUserPassword(g, user, false, group.Password{})
|
err := group.SetUserPassword(g, user, wildcard, group.Password{})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
httpError(w, err)
|
httpError(w, err)
|
||||||
return
|
return
|
||||||
|
|
|
@ -253,6 +253,24 @@ func TestApi(t *testing.T) {
|
||||||
t.Errorf("Users (after delete): %#v", desc.Users)
|
t.Errorf("Users (after delete): %#v", desc.Users)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
resp, err = do("PUT", "/galene-api/v0/.groups/test/.wildcard-user",
|
||||||
|
"application/json", "", "*",
|
||||||
|
`{"permissions": "present"}`)
|
||||||
|
if err != nil || resp.StatusCode != http.StatusCreated {
|
||||||
|
t.Errorf("Create wildcard user: %v %v", err, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
err = getJSON("/galene-api/v0/.groups/test/.wildcard-user", &user)
|
||||||
|
if err != nil {
|
||||||
|
t.Errorf("Get wildcard user: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
resp, err = do("DELETE", "/galene-api/v0/.groups/test/.wildcard-user",
|
||||||
|
"", "", "", "")
|
||||||
|
if err != nil || resp.StatusCode != http.StatusNoContent {
|
||||||
|
t.Errorf("Delete wildcard user: %v %v", err, resp.StatusCode)
|
||||||
|
}
|
||||||
|
|
||||||
if len(desc.AuthKeys) != 1 {
|
if len(desc.AuthKeys) != 1 {
|
||||||
t.Errorf("Keys: %v", len(desc.AuthKeys))
|
t.Errorf("Keys: %v", len(desc.AuthKeys))
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue