mirror of
https://github.com/jech/galene.git
synced 2024-12-26 09:15:46 +01:00
Allow a single wildcard user.
Rename the fallback-users entry to wildcard-user, and only allow a single fallback user. This is missing the HTTP API.
This commit is contained in:
parent
f5279022ce
commit
9eb0364016
7 changed files with 57 additions and 111 deletions
25
README
25
README
|
@ -126,27 +126,32 @@ with the permission `present`:
|
|||
|
||||
{
|
||||
"users":{
|
||||
"jch": {"password":"1234", "permissions": "op"}
|
||||
"jch": {"password": "1234", "permissions": "op"}
|
||||
"john": {"password": "secret", "permissions": "present"}
|
||||
}
|
||||
}
|
||||
|
||||
If the group is to be publicly accessible, you may allow logins with any
|
||||
username using the `fallback-users` entry::
|
||||
username using the `wildcard-user` entry::
|
||||
|
||||
{
|
||||
"users":{
|
||||
"jch": {"password":"1234", "permissions": "op"}
|
||||
},
|
||||
"fallback-users": [
|
||||
{"password": {"type": "wildcard"}, "permissions": "present"}
|
||||
],
|
||||
"wildcard-user": {"password": "1234", "permissions": "present"},
|
||||
"public": true
|
||||
}
|
||||
|
||||
The password `{"type": "wildcard"}` indicates that any password will be
|
||||
accepted.
|
||||
If you want to allow users to use any password, use a wildcard password:
|
||||
|
||||
{
|
||||
"users":{
|
||||
"jch": {"password":"1234", "permissions": "op"}
|
||||
},
|
||||
"wildcard-user":
|
||||
{"password": {"type": "wildcard"}, "permissions": "present"},
|
||||
"public": true
|
||||
}
|
||||
|
||||
## Reference
|
||||
|
||||
|
@ -158,9 +163,9 @@ nobody will be able to join the group. The following fields are allowed:
|
|||
- `users`: is a dictionary that maps user names to dictionaries with
|
||||
entries `password` and `permissions`; `permissions` should be one of
|
||||
`op`, `present` or `passive`;
|
||||
- `fallback-users` is an array of dictionaries with entries `password`
|
||||
and `permissions` that will be used for usernames with no matching
|
||||
entry in the `users` dictionary;
|
||||
- `wildcard-user` is a dictionaries with entries `password` and `permissions`
|
||||
that will be used for usernames with no matching entry in the `users`
|
||||
dictionary;
|
||||
- `authKeys`, `authServer` and `authPortal`: see *Authorisation* below;
|
||||
- `public`: if true, then the group is listed on the landing page;
|
||||
- `displayName`: a human-friendly version of the group name;
|
||||
|
|
23
README.API
23
README.API
|
@ -55,14 +55,6 @@ on-disk format but without any user definitions or cryptographic keys.
|
|||
Allowed methods are HEAD, GET, PUT and DELETE. The only accepted
|
||||
content-type is `application/json`.
|
||||
|
||||
### Fallback users
|
||||
|
||||
/galene-api/v0/.groups/groupname/.fallback-users
|
||||
|
||||
Contains fallback user descriptions, in the same format as the
|
||||
`fallbackUsers` field of the on-disk format. Allowed methods are PUT and
|
||||
DELETE. The only accepted content-type is `application/json`.
|
||||
|
||||
### Authentication keys
|
||||
|
||||
/galene-api/v0/.groups/groupname/.keys
|
||||
|
@ -97,6 +89,21 @@ will be hashed on the server. Allowed methods are PUT, POST and DELETE.
|
|||
Accepted content-types are `application/json` for PUT and `text/plain` for
|
||||
POST.
|
||||
|
||||
### Wildcard user
|
||||
|
||||
/galene-api/v0/.groups/groupname/.wildcard-user
|
||||
|
||||
Contains a dictionary defining the wildcard user, in the same format as
|
||||
the dictionary defining an ordinary user. Allowed methods are HEAD, GET,
|
||||
PUT and DELETE.
|
||||
|
||||
### Wildcard user password
|
||||
|
||||
/galene-api/v0/.groups/groupname/.wildcard-user/.password
|
||||
|
||||
This is analogous to the password of an ordinary user. Allowed methods
|
||||
are PUT, POST and DELETE.
|
||||
|
||||
### List of stateful tokens
|
||||
|
||||
/galene-api/v0/.groups/groupname/.users/username/.tokens/
|
||||
|
|
|
@ -192,8 +192,8 @@ type Description struct {
|
|||
// Users allowed to login
|
||||
Users map[string]UserDescription `json:"users,omitempty"`
|
||||
|
||||
// Credentials for users with arbitrary username
|
||||
FallbackUsers []UserDescription `json:"fallback-users,omitempty"`
|
||||
// Credentials for user with arbitrary username
|
||||
WildcardUser *UserDescription `json:"wildcard-user,omitempty"`
|
||||
|
||||
// The (public) keys used for token authentication.
|
||||
AuthKeys []map[string]interface{} `json:"authKeys,omitempty"`
|
||||
|
@ -298,7 +298,7 @@ func GetSanitisedDescription(name string) (*Description, string, error) {
|
|||
|
||||
desc := *d
|
||||
desc.Users = nil
|
||||
desc.FallbackUsers = nil
|
||||
desc.WildcardUser = nil
|
||||
desc.AuthKeys = nil
|
||||
return &desc, makeETag(desc.fileSize, desc.modTime), nil
|
||||
}
|
||||
|
@ -335,7 +335,7 @@ func DeleteDescription(name, etag string) error {
|
|||
// UpdateDescription overwrites a description if it matches a given ETag.
|
||||
// In order to create a new group, pass an empty ETag.
|
||||
func UpdateDescription(name, etag string, desc *Description) error {
|
||||
if desc.Users != nil || desc.FallbackUsers != nil || desc.AuthKeys != nil {
|
||||
if desc.Users != nil || desc.WildcardUser != nil || desc.AuthKeys != nil {
|
||||
return errors.New("description is not sanitised")
|
||||
}
|
||||
|
||||
|
@ -364,7 +364,7 @@ func UpdateDescription(name, etag string, desc *Description) error {
|
|||
newdesc := *desc
|
||||
if old != nil {
|
||||
newdesc.Users = old.Users
|
||||
newdesc.FallbackUsers = old.FallbackUsers
|
||||
newdesc.WildcardUser = old.WildcardUser
|
||||
newdesc.AuthKeys = old.AuthKeys
|
||||
}
|
||||
|
||||
|
@ -500,8 +500,13 @@ func upgradeDescription(desc *Description) error {
|
|||
}
|
||||
for _, u := range ps {
|
||||
if u.Username == "" {
|
||||
desc.FallbackUsers = append(desc.FallbackUsers,
|
||||
upgradeUser(u, p))
|
||||
if desc.WildcardUser != nil {
|
||||
log.Printf("%v: duplicate wildcard user",
|
||||
desc.FileName)
|
||||
continue
|
||||
}
|
||||
u := upgradeUser(u, p)
|
||||
desc.WildcardUser = &u
|
||||
continue
|
||||
}
|
||||
_, found := desc.Users[u.Username]
|
||||
|
@ -559,7 +564,7 @@ func GetDescriptionNames() ([]string, error) {
|
|||
return names, err
|
||||
}
|
||||
|
||||
func SetFallbackUsers(group string, users []UserDescription) error {
|
||||
func SetWildcardUser(group string, user *UserDescription) error {
|
||||
groups.mu.Lock()
|
||||
defer groups.mu.Unlock()
|
||||
|
||||
|
@ -567,7 +572,7 @@ func SetFallbackUsers(group string, users []UserDescription) error {
|
|||
if err != nil {
|
||||
return err
|
||||
}
|
||||
desc.FallbackUsers = users
|
||||
desc.WildcardUser = user
|
||||
return rewriteDescriptionFile(desc.FileName, desc)
|
||||
}
|
||||
|
||||
|
|
|
@ -66,9 +66,8 @@ var descJSON = `
|
|||
"james": {"password": "secret2", "permissions": "observe"},
|
||||
"peter": {"password": "secret4"}
|
||||
},
|
||||
"fallback-users": [
|
||||
"wildcard-user":
|
||||
{"permissions": "observe", "password": {"type":"wildcard"}}
|
||||
]
|
||||
}`
|
||||
|
||||
func TestDescriptionJSON(t *testing.T) {
|
||||
|
@ -150,19 +149,15 @@ func TestUpgradeDescription(t *testing.T) {
|
|||
}
|
||||
}
|
||||
|
||||
if len(d1.FallbackUsers) != len(d2.FallbackUsers) {
|
||||
t.Errorf("length not equal: %v != %v",
|
||||
len(d1.FallbackUsers), len(d2.FallbackUsers))
|
||||
}
|
||||
|
||||
for k, v1 := range d1.FallbackUsers {
|
||||
v2 := d2.FallbackUsers[k]
|
||||
if !reflect.DeepEqual(v1.Password, v2.Password) ||
|
||||
!permissionsEqual(
|
||||
v1.Permissions.Permissions(&d1),
|
||||
v2.Permissions.Permissions(&d2),
|
||||
) {
|
||||
t.Errorf("%v not equal: %v != %v", k, v1, v2)
|
||||
if d1.WildcardUser != nil || d2.WildcardUser != nil {
|
||||
if !reflect.DeepEqual(
|
||||
d1.WildcardUser.Password, d2.WildcardUser.Password,
|
||||
) || !permissionsEqual(
|
||||
d1.WildcardUser.Permissions.Permissions(&d1),
|
||||
d2.WildcardUser.Permissions.Permissions(&d2),
|
||||
) {
|
||||
t.Errorf("WildcardUser not equal: %v != %v",
|
||||
d1.WildcardUser, d2.WildcardUser)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -942,26 +942,12 @@ func (g *Group) getPasswordPermission(creds ClientCredentials) (Permissions, err
|
|||
}
|
||||
}
|
||||
|
||||
for _, c := range desc.FallbackUsers {
|
||||
if c.Password.Type == "wildcard" {
|
||||
continue
|
||||
}
|
||||
ok, _ := c.Password.Match(creds.Password)
|
||||
if desc.WildcardUser != nil {
|
||||
ok, _ := desc.WildcardUser.Password.Match(creds.Password)
|
||||
if ok {
|
||||
return c.Permissions, nil
|
||||
return desc.WildcardUser.Permissions, nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, c := range desc.FallbackUsers {
|
||||
if c.Password.Type != "wildcard" {
|
||||
continue
|
||||
}
|
||||
ok, _ := c.Password.Match(creds.Password)
|
||||
if ok {
|
||||
return c.Permissions, nil
|
||||
}
|
||||
}
|
||||
|
||||
return Permissions{}, &NotAuthorisedError{}
|
||||
}
|
||||
|
||||
|
|
|
@ -161,9 +161,6 @@ func apiGroupHandler(w http.ResponseWriter, r *http.Request, pth string) {
|
|||
if kind == ".users" {
|
||||
usersHandler(w, r, g, rest)
|
||||
return
|
||||
} else if kind == ".fallback-users" && rest == "" {
|
||||
fallbackUsersHandler(w, r, g)
|
||||
return
|
||||
} else if kind == ".keys" && rest == "" {
|
||||
keysHandler(w, r, g)
|
||||
return
|
||||
|
@ -425,38 +422,6 @@ func passwordHandler(w http.ResponseWriter, r *http.Request, g, user string) {
|
|||
return
|
||||
}
|
||||
|
||||
func fallbackUsersHandler(w http.ResponseWriter, r *http.Request, g string) {
|
||||
if !checkAdmin(w, r) {
|
||||
return
|
||||
}
|
||||
|
||||
if r.Method == "PUT" {
|
||||
var users []group.UserDescription
|
||||
done := getJSON(w, r, &users)
|
||||
if done {
|
||||
return
|
||||
}
|
||||
err := group.SetFallbackUsers(g, users)
|
||||
if err != nil {
|
||||
httpError(w, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
} else if r.Method == "DELETE" {
|
||||
err := group.SetFallbackUsers(g, nil)
|
||||
if err != nil {
|
||||
httpError(w, err)
|
||||
return
|
||||
}
|
||||
w.WriteHeader(http.StatusNoContent)
|
||||
return
|
||||
}
|
||||
|
||||
methodNotAllowed(w, "PUT", "DELETE")
|
||||
return
|
||||
}
|
||||
|
||||
type jwkset = struct {
|
||||
Keys []map[string]any `json:"keys"`
|
||||
}
|
||||
|
|
|
@ -159,13 +159,6 @@ func TestApi(t *testing.T) {
|
|||
t.Errorf("Get groups: %v %v", err, groups)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/galene-api/v0/.groups/test/.fallback-users",
|
||||
"application/json", "", "",
|
||||
`[{"password": "topsecret"}]`)
|
||||
if err != nil || resp.StatusCode != http.StatusNoContent {
|
||||
t.Errorf("Set fallback users: %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/galene-api/v0/.groups/test/.keys",
|
||||
"application/jwk-set+json", "", "",
|
||||
`{"keys": [{
|
||||
|
@ -260,10 +253,6 @@ func TestApi(t *testing.T) {
|
|||
t.Errorf("Users (after delete): %#v", desc.Users)
|
||||
}
|
||||
|
||||
if len(desc.FallbackUsers) != 1 {
|
||||
t.Errorf("Keys: %v", len(desc.AuthKeys))
|
||||
}
|
||||
|
||||
if len(desc.AuthKeys) != 1 {
|
||||
t.Errorf("Keys: %v", len(desc.AuthKeys))
|
||||
}
|
||||
|
@ -344,12 +333,6 @@ func TestApi(t *testing.T) {
|
|||
t.Errorf("Token list: %v %v", tokens, err)
|
||||
}
|
||||
|
||||
resp, err = do("DELETE", "/galene-api/v0/.groups/test/.fallback-users",
|
||||
"", "", "", "")
|
||||
if err != nil || resp.StatusCode != http.StatusNoContent {
|
||||
t.Errorf("Delete fallback users: %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
resp, err = do("DELETE", "/galene-api/v0/.groups/test/.keys",
|
||||
"", "", "", "")
|
||||
if err != nil || resp.StatusCode != http.StatusNoContent {
|
||||
|
|
Loading…
Reference in a new issue