Rename public group URLs to halls
This commit is contained in:
@@ -15,7 +15,7 @@ echo '{"users": {"vimes": {"password":"sybil", "permissions":"op"}}}' > groups/n
|
||||
./skald &
|
||||
```
|
||||
|
||||
Point your browser at <https://localhost:8443/group/night-watch/>, ignore
|
||||
Point your browser at <https://localhost:8443/hall/night-watch/>, ignore
|
||||
the unknown certificate warning, and log in with username *vimes* and
|
||||
password *sybil*.
|
||||
|
||||
|
||||
+21
-21
@@ -1,22 +1,22 @@
|
||||
# Skald's administrative API
|
||||
|
||||
Skald provides an HTTP-based API that can be used to create groups and
|
||||
users. For example, in order to create a group, a client may do
|
||||
users. For example, in order to create a hall, a client may do
|
||||
|
||||
PUT /skald-api/v0/.groups/groupname/
|
||||
PUT /skald-api/v0/.halls/hallname/
|
||||
Content-Type: application/json
|
||||
If-None-Match: *
|
||||
|
||||
The `If-None-Match` header avoids overwriting an existing group.
|
||||
|
||||
In order to edit a group definition, a client first does
|
||||
In order to edit a hall definition, a client first does
|
||||
|
||||
GET /skald-api/v0/.groups/groupname/
|
||||
GET /skald-api/v0/.halls/hallname/
|
||||
|
||||
This yields the group definition and an entity tag (in the ETag header).
|
||||
This yields the hall definition and an entity tag (in the ETag header).
|
||||
The client then modifies the group defintion, and does
|
||||
|
||||
PUT /skald-api/v0/.groups/groupname/
|
||||
PUT /skald-api/v0/.halls/hallname/
|
||||
If-Match: "abcd"
|
||||
|
||||
where "abcd" is the entity tag returned by the GET request. If the group
|
||||
@@ -41,23 +41,23 @@ allowed methods are HEAD and GET.
|
||||
|
||||
### List of groups
|
||||
|
||||
/skald-api/v0/.groups/
|
||||
/skald-api/v0/.halls/
|
||||
|
||||
Returns a list of groups, as a JSON array. The only allowed methods are
|
||||
HEAD and GET.
|
||||
|
||||
### Group definition
|
||||
|
||||
/skald-api/v0/.groups/groupname
|
||||
/skald-api/v0/.halls/hallname
|
||||
|
||||
Contains a "sanitised" group definition in JSON format, analogous to the
|
||||
Contains a "sanitised" hall definition in JSON format, analogous to the
|
||||
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`.
|
||||
|
||||
### Authentication keys
|
||||
|
||||
/skald-api/v0/.groups/groupname/.keys
|
||||
/skald-api/v0/.halls/hallname/.keys
|
||||
|
||||
Contains the keys used for validation of stateless tokens, encoded as
|
||||
a JSON key set (RFC 7517). Allowed methods are PUT and DELETE. The only
|
||||
@@ -65,16 +65,16 @@ accepted content-type is `application/jwk-set+json`.
|
||||
|
||||
### List of users
|
||||
|
||||
/skald-api/v0/.groups/groupname/.users/
|
||||
/skald-api/v0/.halls/hallname/.users/
|
||||
|
||||
Returns a list of users, as a JSON array. The only allowed methods are
|
||||
HEAD and GET.
|
||||
|
||||
### User definitions
|
||||
|
||||
/skald-api/v0/.groups/groupname/.users/username
|
||||
/skald-api/v0/.groups/groupname/.empty-user
|
||||
/skald-api/v0/.groups/groupname/.wildcard-user
|
||||
/skald-api/v0/.halls/hallname/.users/username
|
||||
/skald-api/v0/.halls/hallname/.empty-user
|
||||
/skald-api/v0/.halls/hallname/.wildcard-user
|
||||
|
||||
Contains a "sanitised" user definition (without any passwords), a JSON
|
||||
object with a single field `permissions`. The entries `.empty-user` and
|
||||
@@ -84,9 +84,9 @@ only accepted content-type is `application/json`.
|
||||
|
||||
### Passwords
|
||||
|
||||
/skald-api/v0/.groups/groupname/.users/username/.password
|
||||
/skald-api/v0/.groups/groupname/.empty-user/.password
|
||||
/skald-api/v0/.groups/groupname/.wildcard-user/.password
|
||||
/skald-api/v0/.halls/hallname/.users/username/.password
|
||||
/skald-api/v0/.halls/hallname/.empty-user/.password
|
||||
/skald-api/v0/.halls/hallname/.wildcard-user/.password
|
||||
|
||||
Contains the password of a given user. The PUT method takes a full
|
||||
password definition, identical to what can appear in the `"password"`
|
||||
@@ -97,7 +97,7 @@ POST.
|
||||
|
||||
### Wildcard user
|
||||
|
||||
/skald-api/v0/.groups/groupname/.wildcard-user
|
||||
/skald-api/v0/.halls/hallname/.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,
|
||||
@@ -105,14 +105,14 @@ PUT and DELETE.
|
||||
|
||||
### Wildcard user password
|
||||
|
||||
/skald-api/v0/.groups/groupname/.wildcard-user/.password
|
||||
/skald-api/v0/.halls/hallname/.wildcard-user/.password
|
||||
|
||||
This is analogous to the password of an ordinary user. Allowed methods
|
||||
are PUT, POST and DELETE.
|
||||
|
||||
### List of stateful tokens
|
||||
|
||||
/skald-api/v0/.groups/groupname/.users/username/.tokens/
|
||||
/skald-api/v0/.halls/hallname/.users/username/.tokens/
|
||||
|
||||
GET returns the list of stateful tokens, as a JSON array. POST creates
|
||||
a new token, and returns its name in the `Location` header. Allowed
|
||||
@@ -120,7 +120,7 @@ methods are HEAD, GET and POST.
|
||||
|
||||
### Stateful token
|
||||
|
||||
/skald-api/v0/.groups/groupname/.users/username/.tokens/token
|
||||
/skald-api/v0/.halls/hallname/.users/username/.tokens/token
|
||||
|
||||
The full contents of a single token, in JSON. The exact format may change
|
||||
between versions, so a client should first GET a token, update one or more
|
||||
|
||||
+10
-10
@@ -159,46 +159,46 @@ rsync config.json skald@skald.example.org:data/
|
||||
Create a group:
|
||||
|
||||
```sh
|
||||
skaldctl create-group -group city-watch
|
||||
skaldctl create-hall -hall city-watch
|
||||
```
|
||||
|
||||
If you didn't install a TLS certificate above, you will need to run
|
||||
`skaldctl` with the flag `-insecure`:
|
||||
|
||||
```sh
|
||||
skaldctl -insecure create-group -group city-watch
|
||||
skaldctl -insecure create-hall -hall city-watch
|
||||
```
|
||||
|
||||
Create an "op", a user with group moderation privileges:
|
||||
|
||||
```sh
|
||||
skaldctl create-user -group city-watch -user vimes -permissions op
|
||||
skaldctl create-user -hall city-watch -user vimes -permissions op
|
||||
```
|
||||
|
||||
Set the new user's password:
|
||||
|
||||
```sh
|
||||
skaldctl set-password -group city-watch -user vimes
|
||||
skaldctl set-password -hall city-watch -user vimes
|
||||
```
|
||||
|
||||
You should now be able to test your Skald installation by pointing a web
|
||||
browser at <https://skald.example.org:8443/group/city-watch/>.
|
||||
browser at <https://skald.example.org:8443/hall/city-watch/>.
|
||||
|
||||
Create an ordinary user:
|
||||
|
||||
```sh
|
||||
skaldctl create-user -group city-watch -user fred
|
||||
skaldctl set-password -group city-watch -user fred
|
||||
skaldctl create-user -hall city-watch -user fred
|
||||
skaldctl set-password -hall city-watch -user fred
|
||||
```
|
||||
|
||||
Check the results:
|
||||
|
||||
```sh
|
||||
skaldctl list-groups
|
||||
skaldctl list-users -l -group city-watch
|
||||
skaldctl list-halls
|
||||
skaldctl list-users -l -hall city-watch
|
||||
```
|
||||
|
||||
Type `skaldctl -help`, `skaldctl create-group -help`, etc. for more
|
||||
Type `skaldctl -help`, `skaldctl create-hall -help`, etc. for more
|
||||
information.
|
||||
|
||||
## Advanced configuration
|
||||
|
||||
+4
-4
@@ -30,7 +30,7 @@ server to client direction.
|
||||
|
||||
The client needs to know the location of the group, the (user-visible) URL
|
||||
at which the group is found. This may be obtained either by explicit
|
||||
configuration by the user, or by parsing the `/public-groups.json` file
|
||||
configuration by the user, or by parsing the `/public-halls.json` file
|
||||
which may contain an array of group statuses (see below).
|
||||
|
||||
A client then performs an HTTP GET request on the file `.status` at
|
||||
@@ -558,7 +558,7 @@ a POST request to the authorisation server URL containing in its body
|
||||
a JSON dictionary of the following form:
|
||||
```javascript
|
||||
{
|
||||
"location": "https://skald.example.org/group/groupname/",
|
||||
"location": "https://skald.example.org/hall/hallname/",
|
||||
"username": username,
|
||||
"password": password
|
||||
}
|
||||
@@ -575,7 +575,7 @@ a signed JWT (a "JWS") the body of which has the following form:
|
||||
```javascript
|
||||
{
|
||||
"sub": username,
|
||||
"aud": "https://skald.example.org/group/groupname/",
|
||||
"aud": "https://skald.example.org/hall/hallname/",
|
||||
"permissions": ["present"],
|
||||
"iat": now,
|
||||
"exp": now + 30s,
|
||||
@@ -599,4 +599,4 @@ authentication portal performs authorisation, generates a token as above,
|
||||
then redirects back to the group's URL with the token stores in a URL
|
||||
query parameter named `token`:
|
||||
|
||||
https://skald.example.org/group/groupname/?token=eyJhbG...
|
||||
https://skald.example.org/hall/hallname/?token=eyJhbG...
|
||||
|
||||
@@ -23,7 +23,7 @@ your browser directly at the desired group URL.
|
||||
### The group pages
|
||||
|
||||
A group named e.g. *city-watch* has an associated page
|
||||
`/group/city-watch/`. After login, it presents an interface consisting
|
||||
`/hall/city-watch/`. After login, it presents an interface consisting
|
||||
of three panes:
|
||||
|
||||
- the left side pane contains the list of users who have joined the
|
||||
@@ -71,7 +71,7 @@ a *user menu*, a menu that applies to that specific user.
|
||||
The centre pane is a traditional chat interface, with an input form at the
|
||||
bottom and the chat history above it. Chat history is never saved to
|
||||
disk, and is erased after four hours (or whatever is specified in the
|
||||
`"max-history-age"` field of the group definition).
|
||||
`"max-history-age"` field of the hall definition).
|
||||
|
||||
Double-clicking on a message opens a contextual menu.
|
||||
|
||||
@@ -87,7 +87,7 @@ commands.
|
||||
In order to generate an invitation link, choose the entry *Invite user* in
|
||||
the group menu. This generates a link of the form
|
||||
|
||||
https://skald.example.org:8443/group/city-watch/?token=XXX
|
||||
https://skald.example.org:8443/hall/city-watch/?token=XXX
|
||||
|
||||
where the *XXX* part, known as the *token*, is a shared secret. Such
|
||||
a link allows password-less login to the group, and may therefore be
|
||||
@@ -160,7 +160,7 @@ fields are as follows:
|
||||
only meaningful permission is `"admin"`;
|
||||
|
||||
- `writableGroups`: if true, then the API used by `skaldctl` can be used
|
||||
to modify group definitions; if unset or false, then only read-only
|
||||
to modify hall definitions; if unset or false, then only read-only
|
||||
access is allowed;
|
||||
|
||||
- `allowOrigin` is an array containing the list of HTTP origins that
|
||||
@@ -187,21 +187,21 @@ also be edited manually (there is no need to restart the server).
|
||||
|
||||
#### Creating, modifying, and deleting groups
|
||||
|
||||
A group is created using `skaldctl create-group`:
|
||||
A group is created using `skaldctl create-hall`:
|
||||
|
||||
```sh
|
||||
skaldctl create-group -group city-watch
|
||||
skaldctl create-hall -hall city-watch
|
||||
```
|
||||
|
||||
There are a number of options to customise the behaviour of the group, see
|
||||
`skaldctl create-group -help` for a full list. For example, in order to
|
||||
create a group that allows unrestricted creation of tokens, say:
|
||||
`skaldctl create-hall -help` for a full list. For example, in order to
|
||||
create a hall that allows unrestricted creation of tokens, say:
|
||||
|
||||
```sh
|
||||
skaldctl create-group -group city-watch -unrestricted-tokens
|
||||
skaldctl create-hall -hall city-watch -unrestricted-tokens
|
||||
```
|
||||
|
||||
For more advanced configuration, `skaldctl create-group` can be invoked
|
||||
For more advanced configuration, `skaldctl create-hall` can be invoked
|
||||
with the `-json` flag, in which case it takes a JSON template from standard
|
||||
input. The syntax of a JSON template is just like that of a group
|
||||
definition file (see below), except that it must not contain the fields
|
||||
@@ -209,27 +209,27 @@ definition file (see below), except that it must not contain the fields
|
||||
(see the section *Group description reference* below):
|
||||
|
||||
```sh
|
||||
echo '{"redirect": "https://skald.example.org:8443/group/city-watch/"}' | skaldctl create-group -group amcw -json
|
||||
echo '{"redirect": "https://skald.example.org:8443/hall/city-watch/"}' | skaldctl create-hall -hall amcw -json
|
||||
```
|
||||
|
||||
Groups are modified using `skaldctl update-group`:
|
||||
Groups are modified using `skaldctl update-hall`:
|
||||
|
||||
```sh
|
||||
skaldctl update-group -group city-watch -unrestricted-tokens=false
|
||||
skaldctl update-hall -hall city-watch -unrestricted-tokens=false
|
||||
```
|
||||
|
||||
If a JSON template is provided to `skaldctl update-group`, then it is
|
||||
If a JSON template is provided to `skaldctl update-hall`, then it is
|
||||
merged with the existing group configuration. Entries may be deleted
|
||||
by setting them to `null` in the template:
|
||||
|
||||
```sh
|
||||
echo '{"redirect": null}' | skaldctl update-group -group amcw
|
||||
echo '{"redirect": null}' | skaldctl update-hall -hall amcw
|
||||
```
|
||||
|
||||
A group is deleted using `skaldctl delete-group`:
|
||||
A group is deleted using `skaldctl delete-hall`:
|
||||
|
||||
```sh
|
||||
skaldctl delete-group -group amcw
|
||||
skaldctl delete-hall -hall amcw
|
||||
```
|
||||
|
||||
#### Creating, modifying, and deleting users
|
||||
@@ -237,7 +237,7 @@ skaldctl delete-group -group amcw
|
||||
A user entry is created with the `skaldctl create-user` command:
|
||||
|
||||
```sh
|
||||
skaldctl create-user -group city-watch -user vimes -permissions op
|
||||
skaldctl create-user -hall city-watch -user vimes -permissions op
|
||||
```
|
||||
|
||||
If the `-permissions` flag is not specified, it defaults to `present`,
|
||||
@@ -253,7 +253,7 @@ In order to be useful, a user entry needs to be assigned a password. This
|
||||
is done with the `skaldctl set-password` command:
|
||||
|
||||
```sh
|
||||
skaldctl set-password -group city-watch -user vimes
|
||||
skaldctl set-password -hall city-watch -user vimes
|
||||
```
|
||||
|
||||
#### The fallback user
|
||||
@@ -262,15 +262,15 @@ It is sometimes useful to allow multiple users to log in using the same
|
||||
password. This can be achieved by defining the *wildcard* user:
|
||||
|
||||
```sh
|
||||
skaldctl create-user -group city-watch -wildcard
|
||||
skaldctl set-password -group city-watch -wildcard
|
||||
skaldctl create-user -hall city-watch -wildcard
|
||||
skaldctl set-password -hall city-watch -wildcard
|
||||
```
|
||||
|
||||
For open groups, where any user can login with any password, the wildcard
|
||||
user's password is set to the password of type `wildcard`:
|
||||
|
||||
```sh
|
||||
skaldctl set-password -group city-watch -wildcard -type wildcard
|
||||
skaldctl set-password -hall city-watch -wildcard -type wildcard
|
||||
```
|
||||
|
||||
See the section *Client authorisation* below for more information about
|
||||
@@ -291,7 +291,7 @@ Automatic subgroups are enabled by setting the `"auto-subgroups"`
|
||||
field in the group description:
|
||||
|
||||
```sh
|
||||
skaldctl create-group unseen-university -auto-subgroups
|
||||
skaldctl create-hall unseen-university -auto-subgroups
|
||||
```
|
||||
|
||||
Whenever a user attempts to access a subgroup of `unseen-university`, for
|
||||
@@ -308,8 +308,8 @@ the `skaldctl` utility's `create-token`, `revoke-token`, `delete-token`
|
||||
and `list-tokens` commands:
|
||||
|
||||
```sh
|
||||
skaldctl create-token -group city-watch
|
||||
skaldctl list-tokens -l -group city-watch
|
||||
skaldctl create-token -hall city-watch
|
||||
skaldctl list-tokens -l -hall city-watch
|
||||
```
|
||||
|
||||
A token that is generated with the `-include-subgroups` flag applies to
|
||||
@@ -317,25 +317,25 @@ the whole hierarchy rooted at the given group, including both ordinary
|
||||
groups and automatically generated subgroups.
|
||||
|
||||
```sh
|
||||
skaldctl create-token -group city-watch -include-subgroups
|
||||
skaldctl create-token -hall city-watch -include-subgroups
|
||||
```
|
||||
|
||||
Such a token can be attached to the root of the group hierarchy, and
|
||||
therefore be valid for any group on the server:
|
||||
|
||||
```sh
|
||||
skaldctl create-token -group '' -include-subgroups
|
||||
skaldctl create-token -hall '' -include-subgroups
|
||||
```
|
||||
|
||||
### Group description reference
|
||||
|
||||
The definition for the group called *groupname* is in the file
|
||||
`groups/groupname.json`; it does not contain the group name, which makes
|
||||
it easy to copy or link group definitions. You may use subdirectories:
|
||||
The definition for the group called *hallname* is in the file
|
||||
`groups/hallname.json`; it does not contain the group name, which makes
|
||||
it easy to copy or link hall definitions. You may use subdirectories:
|
||||
a file `groups/teaching/networking.json` defines a group called
|
||||
*teaching/networking*.
|
||||
|
||||
Every group definition file contains a single JSON dictionary. All fields
|
||||
Every hall definition file contains a single JSON dictionary. All fields
|
||||
are optional. The following fields are allowed:
|
||||
|
||||
- `users`: a dictionary that maps user names to user descriptions (see
|
||||
@@ -352,7 +352,7 @@ are optional. The following fields are allowed:
|
||||
displayed at the top of the group page;
|
||||
|
||||
- `description`: a human-readable description of the group; this is
|
||||
displayed on the landing page for public groups;
|
||||
displayed on the landing page for public halls;
|
||||
|
||||
- `contact`: a human-readable contact for this group, such as an e-mail
|
||||
address, ignored by the server;
|
||||
|
||||
+87
-87
@@ -65,25 +65,25 @@ var commands = map[string]command{
|
||||
command: deletePasswordCmd,
|
||||
description: "delete a user's password",
|
||||
},
|
||||
"list-groups": {
|
||||
"list-halls": {
|
||||
command: listGroupsCmd,
|
||||
description: "list groups",
|
||||
description: "list halls",
|
||||
},
|
||||
"show-group": {
|
||||
"show-hall": {
|
||||
command: showGroupCmd,
|
||||
description: "show group definition",
|
||||
description: "show hall definition",
|
||||
},
|
||||
"create-group": {
|
||||
"create-hall": {
|
||||
command: createGroupCmd,
|
||||
description: "create a group",
|
||||
description: "create a hall",
|
||||
},
|
||||
"update-group": {
|
||||
"update-hall": {
|
||||
command: updateGroupCmd,
|
||||
description: "change a group's definition",
|
||||
description: "change a hall's definition",
|
||||
},
|
||||
"delete-group": {
|
||||
"delete-hall": {
|
||||
command: deleteGroupCmd,
|
||||
description: "delete a group",
|
||||
description: "delete a hall",
|
||||
},
|
||||
"list-users": {
|
||||
command: listUsersCmd,
|
||||
@@ -593,7 +593,7 @@ func deleteValue(url string) error {
|
||||
}
|
||||
|
||||
func setPasswordCmd(cmdname string, args []string) {
|
||||
var groupname, username string
|
||||
var hallname, username string
|
||||
var wildcard bool
|
||||
var password, algorithm string
|
||||
var iterations, cost, length, saltlen int
|
||||
@@ -603,7 +603,7 @@ func setPasswordCmd(cmdname string, args []string) {
|
||||
"%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.StringVar(&groupname, "group", "", "group `name`")
|
||||
cmd.StringVar(&hallname, "hall", "", "hall `name`")
|
||||
cmd.StringVar(&username, "user", "", "user `name`")
|
||||
cmd.BoolVar(&wildcard, "wildcard", false, "set wildcard user's password")
|
||||
cmd.StringVar(&password, "password", "", "new `password`")
|
||||
@@ -622,9 +622,9 @@ func setPasswordCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if groupname == "" {
|
||||
if hallname == "" {
|
||||
fmt.Fprintf(cmd.Output(),
|
||||
"Option \"-group\" is required\n")
|
||||
"Option \"-hall\" is required\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -655,12 +655,12 @@ func setPasswordCmd(cmdname string, args []string) {
|
||||
var u string
|
||||
if wildcard {
|
||||
u, err = url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups", groupname,
|
||||
serverURL, "/skald-api/v0/.halls", hallname,
|
||||
".wildcard-user/.password",
|
||||
)
|
||||
} else {
|
||||
u, err = url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups", groupname,
|
||||
serverURL, "/skald-api/v0/.halls", hallname,
|
||||
".users", username, ".password",
|
||||
)
|
||||
}
|
||||
@@ -675,7 +675,7 @@ func setPasswordCmd(cmdname string, args []string) {
|
||||
}
|
||||
|
||||
func deletePasswordCmd(cmdname string, args []string) {
|
||||
var groupname, username string
|
||||
var hallname, username string
|
||||
var wildcard bool
|
||||
|
||||
cmd := flag.NewFlagSet(cmdname, flag.ExitOnError)
|
||||
@@ -683,7 +683,7 @@ func deletePasswordCmd(cmdname string, args []string) {
|
||||
"%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.StringVar(&groupname, "group", "", "group `name`")
|
||||
cmd.StringVar(&hallname, "hall", "", "hall `name`")
|
||||
cmd.StringVar(&username, "user", "", "user `name`")
|
||||
cmd.BoolVar(&wildcard, "wildcard", false, "set wildcard user's password")
|
||||
cmd.Parse(args)
|
||||
@@ -693,9 +693,9 @@ func deletePasswordCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if groupname == "" {
|
||||
if hallname == "" {
|
||||
fmt.Fprintf(cmd.Output(),
|
||||
"Option \"-group\" is required\n")
|
||||
"Option \"-hall\" is required\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -710,12 +710,12 @@ func deletePasswordCmd(cmdname string, args []string) {
|
||||
var err error
|
||||
if wildcard {
|
||||
u, err = url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups", groupname,
|
||||
serverURL, "/skald-api/v0/.halls", hallname,
|
||||
".wildcard-user/.password",
|
||||
)
|
||||
} else {
|
||||
u, err = url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups", groupname,
|
||||
serverURL, "/skald-api/v0/.halls", hallname,
|
||||
".users", username, ".password",
|
||||
)
|
||||
}
|
||||
@@ -796,7 +796,7 @@ func stdinJSON(doit bool) (map[string]any, error) {
|
||||
}
|
||||
|
||||
func createGroupCmd(cmdname string, args []string) {
|
||||
var groupname string
|
||||
var hallname string
|
||||
var unrestrictedTokens, autoSubgroups boolOption
|
||||
var doJSON bool
|
||||
cmd := flag.NewFlagSet(cmdname, flag.ExitOnError)
|
||||
@@ -804,7 +804,7 @@ func createGroupCmd(cmdname string, args []string) {
|
||||
"%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.StringVar(&groupname, "group", "", "group `name`")
|
||||
cmd.StringVar(&hallname, "hall", "", "hall `name`")
|
||||
cmd.Var(&unrestrictedTokens, "unrestricted-tokens",
|
||||
"allow ordinary users to create tokens",
|
||||
)
|
||||
@@ -821,14 +821,14 @@ func createGroupCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if groupname == "" {
|
||||
if hallname == "" {
|
||||
fmt.Fprintf(cmd.Output(),
|
||||
"Option \"-group\" is required\n")
|
||||
"Option \"-hall\" is required\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
u, err := url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups", groupname,
|
||||
serverURL, "/skald-api/v0/.halls", hallname,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Build URL: %v", err)
|
||||
@@ -848,18 +848,18 @@ func createGroupCmd(cmdname string, args []string) {
|
||||
|
||||
err = putJSON(u, data, false)
|
||||
if err != nil {
|
||||
log.Fatalf("Create group: %v", err)
|
||||
log.Fatalf("Create hall: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func deleteGroupCmd(cmdname string, args []string) {
|
||||
var groupname string
|
||||
var hallname string
|
||||
cmd := flag.NewFlagSet(cmdname, flag.ExitOnError)
|
||||
setUsage(cmd, cmdname,
|
||||
"%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.StringVar(&groupname, "group", "", "group `name`")
|
||||
cmd.StringVar(&hallname, "hall", "", "hall `name`")
|
||||
cmd.Parse(args)
|
||||
|
||||
if cmd.NArg() != 0 {
|
||||
@@ -867,14 +867,14 @@ func deleteGroupCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if groupname == "" {
|
||||
if hallname == "" {
|
||||
fmt.Fprintf(cmd.Output(),
|
||||
"Option \"-group\" is required\n")
|
||||
"Option \"-hall\" is required\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
u, err := url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups", groupname,
|
||||
serverURL, "/skald-api/v0/.halls", hallname,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Build URL: %v", err)
|
||||
@@ -882,12 +882,12 @@ func deleteGroupCmd(cmdname string, args []string) {
|
||||
|
||||
err = deleteValue(u)
|
||||
if err != nil {
|
||||
log.Fatalf("Delete group: %v", err)
|
||||
log.Fatalf("Delete hall: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func updateGroupCmd(cmdname string, args []string) {
|
||||
var groupname string
|
||||
var hallname string
|
||||
var unrestrictedTokens, autoSubgroups boolOption
|
||||
var doJSON bool
|
||||
cmd := flag.NewFlagSet(cmdname, flag.ExitOnError)
|
||||
@@ -895,7 +895,7 @@ func updateGroupCmd(cmdname string, args []string) {
|
||||
"%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.StringVar(&groupname, "group", "", "group `name`")
|
||||
cmd.StringVar(&hallname, "hall", "", "hall `name`")
|
||||
cmd.Var(&unrestrictedTokens, "unrestricted-tokens",
|
||||
"allow ordinary users to create tokens",
|
||||
)
|
||||
@@ -913,7 +913,7 @@ func updateGroupCmd(cmdname string, args []string) {
|
||||
}
|
||||
|
||||
u, err := url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups", groupname,
|
||||
serverURL, "/skald-api/v0/.halls", hallname,
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Build URL: %v", err)
|
||||
@@ -1007,25 +1007,25 @@ func match(patterns []string, value string) (bool, error) {
|
||||
}
|
||||
|
||||
func listUsersCmd(cmdname string, args []string) {
|
||||
var groupname string
|
||||
var hallname string
|
||||
var long bool
|
||||
cmd := flag.NewFlagSet(cmdname, flag.ExitOnError)
|
||||
setUsage(cmd, cmdname,
|
||||
"%v [option...] %v [option...] [pattern...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.StringVar(&groupname, "group", "", "group `name`")
|
||||
cmd.StringVar(&hallname, "hall", "", "hall `name`")
|
||||
cmd.BoolVar(&long, "l", false, "display permissions")
|
||||
cmd.Parse(args)
|
||||
patterns := cmd.Args()
|
||||
|
||||
if groupname == "" {
|
||||
if hallname == "" {
|
||||
fmt.Fprintf(cmd.Output(),
|
||||
"Option \"-group\" is required\n")
|
||||
"Option \"-hall\" is required\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
u, err := url.JoinPath(serverURL, "/skald-api/v0/.groups/", groupname,
|
||||
u, err := url.JoinPath(serverURL, "/skald-api/v0/.halls/", hallname,
|
||||
".users/")
|
||||
if err != nil {
|
||||
log.Fatalf("Build URL: %v", err)
|
||||
@@ -1069,21 +1069,21 @@ func listUsersCmd(cmdname string, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
func userURL(wildcard bool, groupname, username string) (string, error) {
|
||||
func userURL(wildcard bool, hallname, username string) (string, error) {
|
||||
if wildcard {
|
||||
return url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups", groupname,
|
||||
serverURL, "/skald-api/v0/.halls", hallname,
|
||||
".wildcard-user",
|
||||
)
|
||||
}
|
||||
return url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups", groupname,
|
||||
serverURL, "/skald-api/v0/.halls", hallname,
|
||||
".users", username,
|
||||
)
|
||||
}
|
||||
|
||||
func createUserCmd(cmdname string, args []string) {
|
||||
var groupname, username string
|
||||
var hallname, username string
|
||||
var wildcard bool
|
||||
var permissions stringOption
|
||||
var doJSON bool
|
||||
@@ -1092,7 +1092,7 @@ func createUserCmd(cmdname string, args []string) {
|
||||
"%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.StringVar(&groupname, "group", "", "group `name`")
|
||||
cmd.StringVar(&hallname, "hall", "", "hall `name`")
|
||||
cmd.StringVar(&username, "user", "", "user `name`")
|
||||
cmd.BoolVar(&wildcard, "wildcard", false, "create the wildcard user")
|
||||
cmd.Var(&permissions, "permissions",
|
||||
@@ -1107,9 +1107,9 @@ func createUserCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if groupname == "" {
|
||||
if hallname == "" {
|
||||
fmt.Fprintf(cmd.Output(),
|
||||
"Option \"-group\" is required\n")
|
||||
"Option \"-hall\" is required\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -1131,7 +1131,7 @@ func createUserCmd(cmdname string, args []string) {
|
||||
}
|
||||
}
|
||||
|
||||
u, err := userURL(wildcard, groupname, username)
|
||||
u, err := userURL(wildcard, hallname, username)
|
||||
if err != nil {
|
||||
log.Fatalf("Build URL: %v", err)
|
||||
}
|
||||
@@ -1155,7 +1155,7 @@ func createUserCmd(cmdname string, args []string) {
|
||||
}
|
||||
|
||||
func updateUserCmd(cmdname string, args []string) {
|
||||
var groupname, username string
|
||||
var hallname, username string
|
||||
var wildcard bool
|
||||
var permissions stringOption
|
||||
var doJSON bool
|
||||
@@ -1164,7 +1164,7 @@ func updateUserCmd(cmdname string, args []string) {
|
||||
"%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.StringVar(&groupname, "group", "", "group `name`")
|
||||
cmd.StringVar(&hallname, "hall", "", "hall `name`")
|
||||
cmd.StringVar(&username, "user", "", "user `name`")
|
||||
cmd.BoolVar(&wildcard, "wildcard", false, "update the wildcard user")
|
||||
cmd.Var(&permissions, "permissions", "permissions")
|
||||
@@ -1175,7 +1175,7 @@ func updateUserCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
u, err := userURL(wildcard, groupname, username)
|
||||
u, err := userURL(wildcard, hallname, username)
|
||||
if err != nil {
|
||||
log.Fatalf("Build URL: %v", err)
|
||||
}
|
||||
@@ -1217,13 +1217,13 @@ func updateUserCmd(cmdname string, args []string) {
|
||||
}
|
||||
|
||||
func deleteUserCmd(cmdname string, args []string) {
|
||||
var groupname, username string
|
||||
var hallname, username string
|
||||
var wildcard bool
|
||||
cmd := flag.NewFlagSet(cmdname, flag.ExitOnError)
|
||||
setUsage(cmd, cmdname, "%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.StringVar(&groupname, "group", "", "group `name`")
|
||||
cmd.StringVar(&hallname, "hall", "", "hall `name`")
|
||||
cmd.StringVar(&username, "user", "", "user `name`")
|
||||
cmd.BoolVar(&wildcard, "wildcard", false, "delete the wildcard user")
|
||||
cmd.Parse(args)
|
||||
@@ -1233,9 +1233,9 @@ func deleteUserCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if groupname == "" {
|
||||
if hallname == "" {
|
||||
fmt.Fprintf(cmd.Output(),
|
||||
"Option \"-group\" is required\n")
|
||||
"Option \"-hall\" is required\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -1250,12 +1250,12 @@ func deleteUserCmd(cmdname string, args []string) {
|
||||
var err error
|
||||
if wildcard {
|
||||
u, err = url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups", groupname,
|
||||
serverURL, "/skald-api/v0/.halls", hallname,
|
||||
".wildcard-user",
|
||||
)
|
||||
} else {
|
||||
u, err = url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups", groupname,
|
||||
serverURL, "/skald-api/v0/.halls", hallname,
|
||||
".users", username,
|
||||
)
|
||||
}
|
||||
@@ -1270,13 +1270,13 @@ func deleteUserCmd(cmdname string, args []string) {
|
||||
}
|
||||
|
||||
func showGroupCmd(cmdname string, args []string) {
|
||||
var groupname string
|
||||
var hallname string
|
||||
cmd := flag.NewFlagSet(cmdname, flag.ExitOnError)
|
||||
setUsage(cmd, cmdname,
|
||||
"%v [option...] %v\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.StringVar(&groupname, "group", "", "group `name`")
|
||||
cmd.StringVar(&hallname, "hall", "", "hall `name`")
|
||||
cmd.Parse(args)
|
||||
|
||||
if cmd.NArg() != 0 {
|
||||
@@ -1284,11 +1284,11 @@ func showGroupCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if groupname == "" {
|
||||
log.Fatal("Option \"-group\" is required.")
|
||||
if hallname == "" {
|
||||
log.Fatal("Option \"-hall\" is required.")
|
||||
}
|
||||
|
||||
u, err := url.JoinPath(serverURL, "/skald-api/v0/.groups/", groupname)
|
||||
u, err := url.JoinPath(serverURL, "/skald-api/v0/.halls/", hallname)
|
||||
if err != nil {
|
||||
log.Fatalf("Build URL: %v", err)
|
||||
}
|
||||
@@ -1296,7 +1296,7 @@ func showGroupCmd(cmdname string, args []string) {
|
||||
var description map[string]any
|
||||
_, err = getJSON(u, &description)
|
||||
if err != nil {
|
||||
log.Fatalf("Get group description: %v", err)
|
||||
log.Fatalf("Get hall description: %v", err)
|
||||
}
|
||||
encoder := json.NewEncoder(os.Stdout)
|
||||
encoder.SetIndent("", " ")
|
||||
@@ -1315,7 +1315,7 @@ func listGroupsCmd(cmdname string, args []string) {
|
||||
cmd.Parse(args)
|
||||
patterns := cmd.Args()
|
||||
|
||||
u, err := url.JoinPath(serverURL, "/skald-api/v0/.groups/")
|
||||
u, err := url.JoinPath(serverURL, "/skald-api/v0/.halls/")
|
||||
if err != nil {
|
||||
log.Fatalf("Build URL: %v", err)
|
||||
}
|
||||
@@ -1323,7 +1323,7 @@ func listGroupsCmd(cmdname string, args []string) {
|
||||
var groups []string
|
||||
_, err = getJSON(u, &groups)
|
||||
if err != nil {
|
||||
log.Fatalf("Get groups: %v", err)
|
||||
log.Fatalf("Get halls: %v", err)
|
||||
}
|
||||
sort.Slice(groups, func(i, j int) bool {
|
||||
return groups[i] < groups[j]
|
||||
@@ -1343,13 +1343,13 @@ func listGroupsCmd(cmdname string, args []string) {
|
||||
}
|
||||
|
||||
func listTokensCmd(cmdname string, args []string) {
|
||||
var groupname stringOption
|
||||
var hallname stringOption
|
||||
var long bool
|
||||
cmd := flag.NewFlagSet(cmdname, flag.ExitOnError)
|
||||
setUsage(cmd, cmdname, "%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.Var(&groupname, "group", "group `name`")
|
||||
cmd.Var(&hallname, "hall", "hall `name`")
|
||||
cmd.BoolVar(&long, "l", false, "display token fields")
|
||||
cmd.Parse(args)
|
||||
|
||||
@@ -1358,14 +1358,14 @@ func listTokensCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !groupname.set {
|
||||
if !hallname.set {
|
||||
fmt.Fprintf(cmd.Output(),
|
||||
"Option \"-group\" is required\n")
|
||||
"Option \"-hall\" is required\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
u, err := url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups/", groupname.value, ".tokens/",
|
||||
serverURL, "/skald-api/v0/.halls/", hallname.value, ".tokens/",
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
@@ -1430,14 +1430,14 @@ func listTokensCmd(cmdname string, args []string) {
|
||||
}
|
||||
|
||||
func createTokenCmd(cmdname string, args []string) {
|
||||
var groupname stringOption
|
||||
var hallname stringOption
|
||||
var username, permissions string
|
||||
var includeSubgroups boolOption
|
||||
cmd := flag.NewFlagSet(cmdname, flag.ExitOnError)
|
||||
setUsage(cmd, cmdname, "%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.Var(&groupname, "group", "group `name`")
|
||||
cmd.Var(&hallname, "hall", "hall `name`")
|
||||
cmd.Var(&includeSubgroups, "include-subgroups", "include subgroups")
|
||||
cmd.StringVar(&username, "user", "", "encode user `name` in token")
|
||||
cmd.StringVar(&permissions, "permissions", "present", "permissions")
|
||||
@@ -1448,9 +1448,9 @@ func createTokenCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !groupname.set {
|
||||
if !hallname.set {
|
||||
fmt.Fprintf(cmd.Output(),
|
||||
"Option \"-group\" is required\n")
|
||||
"Option \"-hall\" is required\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
@@ -1469,7 +1469,7 @@ func createTokenCmd(cmdname string, args []string) {
|
||||
}
|
||||
|
||||
u, err := url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups/", groupname.value, ".tokens/",
|
||||
serverURL, "/skald-api/v0/.halls/", hallname.value, ".tokens/",
|
||||
)
|
||||
if err != nil {
|
||||
log.Fatalf("Build URL: %v", err)
|
||||
@@ -1483,13 +1483,13 @@ func createTokenCmd(cmdname string, args []string) {
|
||||
}
|
||||
|
||||
func revokeTokenCmd(cmdname string, args []string) {
|
||||
var groupname stringOption
|
||||
var hallname stringOption
|
||||
var token string
|
||||
cmd := flag.NewFlagSet(cmdname, flag.ExitOnError)
|
||||
setUsage(cmd, cmdname, "%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.Var(&groupname, "group", "group `name`")
|
||||
cmd.Var(&hallname, "hall", "hall `name`")
|
||||
cmd.StringVar(&token, "token", "", "`token` to delete")
|
||||
cmd.Parse(args)
|
||||
|
||||
@@ -1498,14 +1498,14 @@ func revokeTokenCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !groupname.set || token == "" {
|
||||
if !hallname.set || token == "" {
|
||||
fmt.Fprintf(cmd.Output(),
|
||||
"Options \"-group\" and \"-token\" are required\n")
|
||||
"Options \"-hall\" and \"-token\" are required\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
u, err := url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups/", groupname.value,
|
||||
serverURL, "/skald-api/v0/.halls/", hallname.value,
|
||||
".tokens", token,
|
||||
)
|
||||
if err != nil {
|
||||
@@ -1522,13 +1522,13 @@ func revokeTokenCmd(cmdname string, args []string) {
|
||||
}
|
||||
|
||||
func deleteTokenCmd(cmdname string, args []string) {
|
||||
var groupname stringOption
|
||||
var hallname stringOption
|
||||
var token string
|
||||
cmd := flag.NewFlagSet(cmdname, flag.ExitOnError)
|
||||
setUsage(cmd, cmdname, "%v [option...] %v [option...]\n",
|
||||
os.Args[0], cmdname,
|
||||
)
|
||||
cmd.Var(&groupname, "group", "group `name`")
|
||||
cmd.Var(&hallname, "hall", "hall `name`")
|
||||
cmd.StringVar(&token, "token", "", "`token` to delete")
|
||||
cmd.Parse(args)
|
||||
|
||||
@@ -1537,14 +1537,14 @@ func deleteTokenCmd(cmdname string, args []string) {
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !groupname.set || token == "" {
|
||||
if !hallname.set || token == "" {
|
||||
fmt.Fprintf(cmd.Output(),
|
||||
"Options \"-group\" and \"-token\" are required\n")
|
||||
"Options \"-hall\" and \"-token\" are required\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
u, err := url.JoinPath(
|
||||
serverURL, "/skald-api/v0/.groups/", groupname.value,
|
||||
serverURL, "/skald-api/v0/.halls/", hallname.value,
|
||||
".tokens", token,
|
||||
)
|
||||
if err != nil {
|
||||
|
||||
@@ -307,7 +307,7 @@ document.getElementById('start').onclick = async function(e) {
|
||||
let button = /** @type{HTMLButtonElement} */(this);
|
||||
button.hidden = true;
|
||||
try {
|
||||
await start("/group/public/");
|
||||
await start("/hall/public/");
|
||||
} catch(e) {
|
||||
displayError(e);
|
||||
};
|
||||
|
||||
+7
-7
@@ -15,22 +15,22 @@
|
||||
<div class="home">
|
||||
<h1 id="title" class="navbar-brand">Skald</h1>
|
||||
|
||||
<form id="groupform">
|
||||
<label for="group">Group:</label>
|
||||
<input id="group" type="text" name="group" class="form-control form-control-inline" autofocus/>
|
||||
<form id="hallform">
|
||||
<label for="hall">Hall:</label>
|
||||
<input id="hall" type="text" name="hall" class="form-control form-control-inline" autofocus/>
|
||||
<input id='submitbutton' type="submit" value="Join" class="btn btn-default btn-large"/><br/>
|
||||
</form>
|
||||
|
||||
<p id="errormessage"></p>
|
||||
|
||||
<div id="public-groups" class="groups">
|
||||
<h2>Public groups</h2>
|
||||
<div id="public-halls" class="groups">
|
||||
<h2>Public halls</h2>
|
||||
|
||||
<table id="public-groups-table"></table>
|
||||
<table id="public-halls-table"></table>
|
||||
</div>
|
||||
</div>
|
||||
<footer class="signature">
|
||||
<p><a href="https://skald.org/">Skald</a> by <a href="https://www.irif.fr/~jch/" rel="author">Juliusz Chroboczek</a></p>
|
||||
<p>Skald is derived from <a href="https://galene.org/">Galene</a> by <a href="https://www.irif.fr/~jch/" rel="author">Juliusz Chroboczek</a></p>
|
||||
</footer>
|
||||
|
||||
<script src="/mainpage.js" defer></script>
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ body {
|
||||
padding: 1.875rem;
|
||||
}
|
||||
|
||||
#public-groups-table tr a{
|
||||
#public-halls-table tr a{
|
||||
margin-left: 0.9375rem;
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
+10
-10
@@ -20,16 +20,16 @@
|
||||
|
||||
'use strict';
|
||||
|
||||
document.getElementById('groupform').onsubmit = async function(e) {
|
||||
document.getElementById('hallform').onsubmit = async function(e) {
|
||||
e.preventDefault();
|
||||
clearError();
|
||||
let groupinput = document.getElementById('group')
|
||||
let groupinput = document.getElementById('hall')
|
||||
let button = document.getElementById('submitbutton');
|
||||
|
||||
let group = groupinput.value.trim();
|
||||
if(group === '')
|
||||
return;
|
||||
let url = '/group/' + group + '/';
|
||||
let url = '/hall/' + group + '/';
|
||||
let statusUrl = url + '.status.json';
|
||||
|
||||
try {
|
||||
@@ -78,25 +78,25 @@ function clearError() {
|
||||
}
|
||||
}
|
||||
|
||||
async function listPublicGroups() {
|
||||
let div = document.getElementById('public-groups');
|
||||
let table = document.getElementById('public-groups-table');
|
||||
async function listPublicHalls() {
|
||||
let div = document.getElementById('public-halls');
|
||||
let table = document.getElementById('public-halls-table');
|
||||
|
||||
let l;
|
||||
try {
|
||||
let r = await fetch('/public-groups.json');
|
||||
let r = await fetch('/public-halls.json');
|
||||
if(!r.ok)
|
||||
throw new Error(`${r.status} ${r.statusText}`);
|
||||
l = await r.json();
|
||||
} catch(e) {
|
||||
table.textContent = `Couldn't fetch groups: ${e}`;
|
||||
table.textContent = `Couldn't fetch halls: ${e}`;
|
||||
div.classList.remove('nogroups');
|
||||
div.classList.add('groups');
|
||||
return;
|
||||
}
|
||||
|
||||
if (l.length === 0) {
|
||||
table.textContent = '(No groups found.)';
|
||||
table.textContent = '(No halls found.)';
|
||||
div.classList.remove('groups');
|
||||
div.classList.add('nogroups');
|
||||
return;
|
||||
@@ -131,4 +131,4 @@ async function listPublicGroups() {
|
||||
}
|
||||
|
||||
|
||||
listPublicGroups();
|
||||
listPublicHalls();
|
||||
|
||||
+14
-14
@@ -159,7 +159,7 @@ async function updateObject(url, values, etag) {
|
||||
* @returns {Promise<Array<string>>}
|
||||
*/
|
||||
async function listGroups() {
|
||||
return await listObjects('/skald-api/v0/.groups/');
|
||||
return await listObjects('/skald-api/v0/.halls/');
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -170,7 +170,7 @@ async function listGroups() {
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
async function getGroup(group, etag) {
|
||||
return await getObject(`/skald-api/v0/.groups/${group}`, etag);
|
||||
return await getObject(`/skald-api/v0/.halls/${group}`, etag);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -180,7 +180,7 @@ async function getGroup(group, etag) {
|
||||
* @param {Object} [values]
|
||||
*/
|
||||
async function createGroup(group, values) {
|
||||
return await createObject(`/skald-api/v0/.groups/${group}`, values);
|
||||
return await createObject(`/skald-api/v0/.halls/${group}`, values);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -190,11 +190,11 @@ async function createGroup(group, values) {
|
||||
* @param {string} [etag]
|
||||
*/
|
||||
async function deleteGroup(group, etag) {
|
||||
return await deleteObject(`/skald-api/v0/.groups/${group}`, etag);
|
||||
return await deleteObject(`/skald-api/v0/.halls/${group}`, etag);
|
||||
}
|
||||
|
||||
/**
|
||||
* updateGroup modifies a group definition.
|
||||
* updateGroup modifies a hall definition.
|
||||
* Any fields present in values are overriden, any fields absent in values
|
||||
* are left unchanged.
|
||||
*
|
||||
@@ -203,7 +203,7 @@ async function deleteGroup(group, etag) {
|
||||
* @param {string} [etag]
|
||||
*/
|
||||
async function updateGroup(group, values, etag) {
|
||||
return await updateObject(`/skald-api/v0/.groups/${group}`, values);
|
||||
return await updateObject(`/skald-api/v0/.halls/${group}`, values);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -213,7 +213,7 @@ async function updateGroup(group, values, etag) {
|
||||
* @returns {Promise<Array<string>>}
|
||||
*/
|
||||
async function listUsers(group) {
|
||||
return await listObjects(`/skald-api/v0/.groups/${group}/.users/`);
|
||||
return await listObjects(`/skald-api/v0/.halls/${group}/.users/`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -225,11 +225,11 @@ async function listUsers(group) {
|
||||
*/
|
||||
function userURL(group, user, wildcard) {
|
||||
if(wildcard)
|
||||
return `/skald-api/v0/.groups/${group}/.wildcard-user`;
|
||||
return `/skald-api/v0/.halls/${group}/.wildcard-user`;
|
||||
else if(user === "")
|
||||
return `/skald-api/v0/.groups/${group}/.empty-user`;
|
||||
return `/skald-api/v0/.halls/${group}/.empty-user`;
|
||||
else
|
||||
return `/skald-api/v0/.groups/${group}/.users/${user}`
|
||||
return `/skald-api/v0/.halls/${group}/.users/${user}`
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -320,7 +320,7 @@ async function setPassword(group, user, wildcard, password, oldpassword) {
|
||||
* @returns {Promise<Array<string>>}
|
||||
*/
|
||||
async function listTokens(group) {
|
||||
return await listObjects(`/skald-api/v0/.groups/${group}/.tokens/`);
|
||||
return await listObjects(`/skald-api/v0/.halls/${group}/.tokens/`);
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -332,7 +332,7 @@ async function listTokens(group) {
|
||||
* @returns {Promise<Object>}
|
||||
*/
|
||||
async function getToken(group, token, etag) {
|
||||
return await getObject(`/skald-api/v0/.groups/${group}/.tokens/${token}`,
|
||||
return await getObject(`/skald-api/v0/.halls/${group}/.tokens/${token}`,
|
||||
etag);
|
||||
}
|
||||
|
||||
@@ -353,7 +353,7 @@ async function createToken(group, template) {
|
||||
}
|
||||
|
||||
let r = await fetch(
|
||||
`/skald-api/v0/.groups/${group}/.tokens/`,
|
||||
`/skald-api/v0/.halls/${group}/.tokens/`,
|
||||
options);
|
||||
if(!r.ok)
|
||||
throw httpError(r);
|
||||
@@ -373,6 +373,6 @@ async function updateToken(group, token, etag) {
|
||||
if(!token.token)
|
||||
throw new Error("Unnamed token");
|
||||
return await updateObject(
|
||||
`/skald-api/v0/.groups/${group}/.tokens/${token.token}`,
|
||||
`/skald-api/v0/.halls/${group}/.tokens/${token.token}`,
|
||||
token, etag);
|
||||
}
|
||||
|
||||
@@ -83,20 +83,20 @@ X Search for remaining Galene names and classify each survivor as attribution/hi
|
||||
# PHASE 3: URLS, API, PROTOCOL, AND CONTROL TOOL
|
||||
# ============================================================================
|
||||
|
||||
- Rename browser hall URLs from /group/name/ to /hall/name/
|
||||
- Rename /public-groups.json to /public-halls.json
|
||||
- Rename /galene-api/ to /skald-api/
|
||||
- Rename API collection paths from .groups to .halls
|
||||
X Rename browser hall URLs from /group/name/ to /hall/name/
|
||||
X Rename /public-groups.json to /public-halls.json
|
||||
X Rename /galene-api/ to /skald-api/
|
||||
X Rename API collection paths from .groups to .halls
|
||||
- Rename protocol join fields from group to hall
|
||||
- Rename protocol callbacks/events from joined group semantics to joined hall semantics
|
||||
- Update static/protocol.js public API: ServerConnection.join and related callbacks
|
||||
- Update static/management.js to use /skald-api/v0/.halls/
|
||||
- Update static/mainpage.js to open /hall/ URLs and fetch /public-halls.json
|
||||
X Update static/management.js to use /skald-api/v0/.halls/
|
||||
X Update static/mainpage.js to open /hall/ URLs and fetch /public-halls.json
|
||||
- Update stats endpoint naming only if it exposes Skald/group terminology
|
||||
- Update skaldctl command names: create-hall, update-hall, delete-hall, list-halls
|
||||
X Update skaldctl command names: create-hall, update-hall, delete-hall, list-halls
|
||||
- Update skaldctl flags: -hall, -include-subhalls, -auto-subhalls, etc.
|
||||
- Update skaldctl help text and error messages
|
||||
- Update API and webserver tests for /hall/ and /skald-api/
|
||||
X Update skaldctl help text and error messages
|
||||
X Update API and webserver tests for /hall/ and /skald-api/
|
||||
- Verify old /group/, /public-groups.json, /galene-api/, and .groups paths fail clearly
|
||||
- Verify protocol backward compatibility with Skald is intentionally broken
|
||||
|
||||
|
||||
+3
-3
@@ -190,10 +190,10 @@ func parseJWT(token string, keys []map[string]any) (*JWT, error) {
|
||||
|
||||
func matchGroup(pth, group string, includeSubgroups bool) bool {
|
||||
if !includeSubgroups {
|
||||
return pth == "/group/"+group+"/"
|
||||
return pth == "/hall/"+group+"/"
|
||||
}
|
||||
|
||||
if !strings.HasPrefix(pth, "/group/") {
|
||||
if !strings.HasPrefix(pth, "/hall/") {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -201,7 +201,7 @@ func matchGroup(pth, group string, includeSubgroups bool) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.HasPrefix("/group/"+group+"/", pth)
|
||||
return strings.HasPrefix("/hall/"+group+"/", pth)
|
||||
}
|
||||
|
||||
func (token *JWT) Check(host, group string, username *string) (string, []string, error) {
|
||||
|
||||
+13
-13
@@ -85,21 +85,21 @@ func TestMatchGroup(t *testing.T) {
|
||||
s bool
|
||||
}
|
||||
good := []tt{
|
||||
{"/group/a/", "a", false},
|
||||
{"/group/a/b/", "a/b", false},
|
||||
{"/group/a/", "a", true},
|
||||
{"/group/a/", "a/b", true},
|
||||
{"/group/a/b/", "a/b", true},
|
||||
{"/group/a/b/", "a/b/c", true},
|
||||
{"/hall/a/", "a", false},
|
||||
{"/hall/a/b/", "a/b", false},
|
||||
{"/hall/a/", "a", true},
|
||||
{"/hall/a/", "a/b", true},
|
||||
{"/hall/a/b/", "a/b", true},
|
||||
{"/hall/a/b/", "a/b/c", true},
|
||||
}
|
||||
|
||||
bad := []tt{
|
||||
{"/group/a/", "b", false},
|
||||
{"/group/a", "a", false},
|
||||
{"/group/a", "a", true},
|
||||
{"/group/a/", "a/b", false},
|
||||
{"/group/a/b/", "a", false},
|
||||
{"/group/a/b/", "a", true},
|
||||
{"/hall/a/", "b", false},
|
||||
{"/hall/a", "a", false},
|
||||
{"/hall/a", "a", true},
|
||||
{"/hall/a/", "a/b", false},
|
||||
{"/hall/a/b/", "a", false},
|
||||
{"/hall/a/b/", "a", true},
|
||||
}
|
||||
|
||||
for _, test := range good {
|
||||
@@ -142,7 +142,7 @@ func TestJWT(t *testing.T) {
|
||||
john := "john"
|
||||
jack := "jack"
|
||||
baseClaims := jwt.MapClaims{
|
||||
"aud": "https://skald.org:8443/group/auth/",
|
||||
"aud": "https://skald.org:8443/hall/auth/",
|
||||
"permissions": []string{"present"},
|
||||
"iat": int64(1645310294),
|
||||
"exp": int64(2906750294),
|
||||
|
||||
+16
-16
@@ -46,27 +46,27 @@ func TestStatefulCheck(t *testing.T) {
|
||||
user2 := "user2"
|
||||
token1 := &Stateful{
|
||||
Token: "token",
|
||||
Group: "group",
|
||||
Group: "hall",
|
||||
Username: &user,
|
||||
Permissions: []string{"present", "message"},
|
||||
Expires: &future,
|
||||
}
|
||||
token2 := &Stateful{
|
||||
Token: "token",
|
||||
Group: "group",
|
||||
Group: "hall",
|
||||
Permissions: []string{"present", "message"},
|
||||
Expires: &future,
|
||||
}
|
||||
token3 := &Stateful{
|
||||
Token: "token",
|
||||
Group: "group",
|
||||
Group: "hall",
|
||||
Username: &user,
|
||||
Permissions: []string{"present", "message"},
|
||||
Expires: &past,
|
||||
}
|
||||
token4 := &Stateful{
|
||||
Token: "token",
|
||||
Group: "group",
|
||||
Group: "hall",
|
||||
Username: &user,
|
||||
Permissions: []string{"present", "message"},
|
||||
Expires: &future,
|
||||
@@ -74,7 +74,7 @@ func TestStatefulCheck(t *testing.T) {
|
||||
}
|
||||
token5 := &Stateful{
|
||||
Token: "token",
|
||||
Group: "group",
|
||||
Group: "hall",
|
||||
IncludeSubgroups: true,
|
||||
Username: &user,
|
||||
Permissions: []string{"present", "message"},
|
||||
@@ -106,48 +106,48 @@ func TestStatefulCheck(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
token: token1,
|
||||
group: "group",
|
||||
group: "hall",
|
||||
username: &user,
|
||||
expUsername: user,
|
||||
expPermissions: []string{"present", "message"},
|
||||
},
|
||||
{
|
||||
token: token1,
|
||||
group: "group",
|
||||
group: "hall",
|
||||
username: &user2,
|
||||
expUsername: user,
|
||||
expPermissions: []string{"present", "message"},
|
||||
},
|
||||
{
|
||||
token: token1,
|
||||
group: "group",
|
||||
group: "hall",
|
||||
expUsername: user,
|
||||
expPermissions: []string{"present", "message"},
|
||||
},
|
||||
{
|
||||
token: token2,
|
||||
group: "group",
|
||||
group: "hall",
|
||||
username: &user,
|
||||
expUsername: "",
|
||||
expPermissions: []string{"present", "message"},
|
||||
},
|
||||
{
|
||||
token: token5,
|
||||
group: "group",
|
||||
group: "hall",
|
||||
username: &user,
|
||||
expUsername: "user",
|
||||
expPermissions: []string{"present", "message"},
|
||||
},
|
||||
{
|
||||
token: token5,
|
||||
group: "group/subgroup",
|
||||
group: "hall/subhall",
|
||||
username: &user,
|
||||
expUsername: "user",
|
||||
expPermissions: []string{"present", "message"},
|
||||
},
|
||||
{
|
||||
token: token7,
|
||||
group: "group/subgroup",
|
||||
group: "hall/subhall",
|
||||
username: &user,
|
||||
expUsername: "user",
|
||||
expPermissions: []string{"present", "message"},
|
||||
@@ -171,17 +171,17 @@ func TestStatefulCheck(t *testing.T) {
|
||||
}{
|
||||
{
|
||||
token: token1,
|
||||
group: "group2",
|
||||
group: "hall2",
|
||||
username: &user,
|
||||
},
|
||||
{
|
||||
token: token3,
|
||||
group: "group",
|
||||
group: "hall",
|
||||
username: &user,
|
||||
},
|
||||
{
|
||||
token: token4,
|
||||
group: "group",
|
||||
group: "hall",
|
||||
username: &user,
|
||||
},
|
||||
{
|
||||
@@ -196,7 +196,7 @@ func TestStatefulCheck(t *testing.T) {
|
||||
},
|
||||
{
|
||||
token: token6,
|
||||
group: "group",
|
||||
group: "hall",
|
||||
username: &user,
|
||||
},
|
||||
{
|
||||
|
||||
+2
-2
@@ -25,7 +25,7 @@ func TestToken(t *testing.T) {
|
||||
user := "user"
|
||||
_, err = Update(&Stateful{
|
||||
Token: "token",
|
||||
Group: "group",
|
||||
Group: "hall",
|
||||
Username: &user,
|
||||
Permissions: []string{"present"},
|
||||
Expires: &future,
|
||||
@@ -39,7 +39,7 @@ func TestToken(t *testing.T) {
|
||||
t.Fatalf("Parse: %v", err)
|
||||
}
|
||||
|
||||
_, _, err = token.Check("skald.org:8443", "group", &user)
|
||||
_, _, err = token.Check("skald.org:8443", "hall", &user)
|
||||
if err != nil {
|
||||
t.Errorf("Check: %v", err)
|
||||
}
|
||||
|
||||
+4
-4
@@ -34,7 +34,7 @@ func checkAdmin(w http.ResponseWriter, r *http.Request) bool {
|
||||
// checkPasswordAdmin checks whether the client authentifies as either an
|
||||
// administrator or the given user. It is used to check whether the
|
||||
// client has the right to change user's password.
|
||||
func checkPasswordAdmin(w http.ResponseWriter, r *http.Request, groupname, user string, wildcard bool) bool {
|
||||
func checkPasswordAdmin(w http.ResponseWriter, r *http.Request, hallname, user string, wildcard bool) bool {
|
||||
username, password, ok := r.BasicAuth()
|
||||
if ok {
|
||||
ok, err := adminMatch(username, password)
|
||||
@@ -47,11 +47,11 @@ func checkPasswordAdmin(w http.ResponseWriter, r *http.Request, groupname, user
|
||||
}
|
||||
}
|
||||
if ok && !wildcard && username == user {
|
||||
desc, err := group.GetDescription(groupname)
|
||||
desc, err := group.GetDescription(hallname)
|
||||
if err != nil {
|
||||
internalError(w,
|
||||
"Get description for group %v: %v",
|
||||
groupname, err,
|
||||
hallname, err,
|
||||
)
|
||||
return false
|
||||
}
|
||||
@@ -169,7 +169,7 @@ func apiHandler(w http.ResponseWriter, r *http.Request) {
|
||||
}
|
||||
w.Header().Set("cache-control", "no-cache")
|
||||
sendJSON(w, r, stats.GetGroups())
|
||||
case ".groups":
|
||||
case ".halls":
|
||||
apiGroupHandler(w, r, rest)
|
||||
default:
|
||||
http.NotFound(w, r)
|
||||
|
||||
+59
-59
@@ -115,58 +115,58 @@ func TestApi(t *testing.T) {
|
||||
}
|
||||
|
||||
var groups []string
|
||||
err = getJSON("/skald-api/v0/.groups/", &groups)
|
||||
err = getJSON("/skald-api/v0/.halls/", &groups)
|
||||
if err != nil || len(groups) != 0 {
|
||||
t.Errorf("Get groups: %v", err)
|
||||
t.Errorf("Get halls: %v", err)
|
||||
}
|
||||
|
||||
resp, err := do("PUT", "/skald-api/v0/.groups/test/",
|
||||
resp, err := do("PUT", "/skald-api/v0/.halls/test/",
|
||||
"application/json", "\"foo\"", "",
|
||||
"{}")
|
||||
if err != nil || resp.StatusCode != http.StatusPreconditionFailed {
|
||||
t.Errorf("Create group (bad ETag): %v %v", err, resp.StatusCode)
|
||||
t.Errorf("Create hall (bad ETag): %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/skald-api/v0/.groups/test/",
|
||||
resp, err = do("PUT", "/skald-api/v0/.halls/test/",
|
||||
"text/plain", "", "",
|
||||
"Hello, world!")
|
||||
if err != nil || resp.StatusCode != http.StatusUnsupportedMediaType {
|
||||
t.Errorf("Create group (bad content-type): %v %v",
|
||||
t.Errorf("Create hall (bad content-type): %v %v",
|
||||
err, resp.StatusCode)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/skald-api/v0/.groups/test/",
|
||||
resp, err = do("PUT", "/skald-api/v0/.halls/test/",
|
||||
"application/json", "", "*",
|
||||
"{}")
|
||||
if err != nil || resp.StatusCode != http.StatusCreated {
|
||||
t.Errorf("Create group: %v %v", err, resp.StatusCode)
|
||||
t.Errorf("Create hall: %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
var desc *group.Description
|
||||
err = getJSON("/skald-api/v0/.groups/test/", &desc)
|
||||
err = getJSON("/skald-api/v0/.halls/test/", &desc)
|
||||
if err != nil || len(desc.Users) != 0 {
|
||||
t.Errorf("Get group: %v", err)
|
||||
t.Errorf("Get hall: %v", err)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/skald-api/v0/.groups/test/",
|
||||
resp, err = do("PUT", "/skald-api/v0/.halls/test/",
|
||||
"application/json", "", "*",
|
||||
"{}")
|
||||
if err != nil || resp.StatusCode != http.StatusPreconditionFailed {
|
||||
t.Errorf("Create group (bad ETag): %v %v", err, resp.StatusCode)
|
||||
t.Errorf("Create hall (bad ETag): %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
resp, err = do("DELETE", "/skald-api/v0/.groups/test/",
|
||||
resp, err = do("DELETE", "/skald-api/v0/.halls/test/",
|
||||
"", "", "*", "")
|
||||
if err != nil || resp.StatusCode != http.StatusPreconditionFailed {
|
||||
t.Errorf("Delete group (bad ETag): %v %v", err, resp.StatusCode)
|
||||
t.Errorf("Delete hall (bad ETag): %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
err = getJSON("/skald-api/v0/.groups/", &groups)
|
||||
err = getJSON("/skald-api/v0/.halls/", &groups)
|
||||
if err != nil || len(groups) != 1 || groups[0] != "test" {
|
||||
t.Errorf("Get groups: %v %v", err, groups)
|
||||
t.Errorf("Get halls: %v %v", err, groups)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/skald-api/v0/.groups/test/.keys",
|
||||
resp, err = do("PUT", "/skald-api/v0/.halls/test/.keys",
|
||||
"application/jwk-set+json", "", "",
|
||||
`{"keys": [{
|
||||
"kty": "oct", "alg": "HS256",
|
||||
@@ -176,12 +176,12 @@ func TestApi(t *testing.T) {
|
||||
t.Errorf("Set key: %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
err = getJSON("/skald-api/v0/.groups/test/.users/", &groups)
|
||||
err = getJSON("/skald-api/v0/.halls/test/.users/", &groups)
|
||||
if err != nil || len(groups) != 0 {
|
||||
t.Errorf("Get users: %v", err)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/skald-api/v0/.groups/test/.users/jch",
|
||||
resp, err = do("PUT", "/skald-api/v0/.halls/test/.users/jch",
|
||||
"text/plain", "", "*",
|
||||
`hello, world!`)
|
||||
if err != nil || resp.StatusCode != http.StatusUnsupportedMediaType {
|
||||
@@ -189,7 +189,7 @@ func TestApi(t *testing.T) {
|
||||
err, resp.StatusCode)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/skald-api/v0/.groups/test/.users/jch",
|
||||
resp, err = do("PUT", "/skald-api/v0/.halls/test/.users/jch",
|
||||
"application/json", "", "*",
|
||||
`{"permissions": "present"}`)
|
||||
if err != nil || resp.StatusCode != http.StatusCreated {
|
||||
@@ -197,26 +197,26 @@ func TestApi(t *testing.T) {
|
||||
}
|
||||
|
||||
var users []string
|
||||
err = getJSON("/skald-api/v0/.groups/test/.users/", &users)
|
||||
err = getJSON("/skald-api/v0/.halls/test/.users/", &users)
|
||||
if err != nil || len(users) != 1 || users[0] != "jch" {
|
||||
t.Errorf("Get users: %v %v", err, users)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/skald-api/v0/.groups/test/.users/jch",
|
||||
resp, err = do("PUT", "/skald-api/v0/.halls/test/.users/jch",
|
||||
"application/json", "", "*",
|
||||
`{"permissions": "present"}`)
|
||||
if err != nil || resp.StatusCode != http.StatusPreconditionFailed {
|
||||
t.Errorf("Create user (bad ETag): %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/skald-api/v0/.groups/test/.users/jch/.password",
|
||||
resp, err = do("PUT", "/skald-api/v0/.halls/test/.users/jch/.password",
|
||||
"application/json", "", "",
|
||||
`"toto"`)
|
||||
if err != nil || resp.StatusCode != http.StatusNoContent {
|
||||
t.Errorf("Set password (PUT): %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
resp, err = do("POST", "/skald-api/v0/.groups/test/.users/jch/.password",
|
||||
resp, err = do("POST", "/skald-api/v0/.halls/test/.users/jch/.password",
|
||||
"text/plain", "", "",
|
||||
`toto`)
|
||||
if err != nil || resp.StatusCode != http.StatusNoContent {
|
||||
@@ -224,7 +224,7 @@ func TestApi(t *testing.T) {
|
||||
}
|
||||
|
||||
var user group.UserDescription
|
||||
err = getJSON("/skald-api/v0/.groups/test/.users/jch", &user)
|
||||
err = getJSON("/skald-api/v0/.halls/test/.users/jch", &user)
|
||||
if err != nil {
|
||||
t.Errorf("Get user: %v", err)
|
||||
}
|
||||
@@ -245,10 +245,10 @@ func TestApi(t *testing.T) {
|
||||
t.Errorf("Password.Type: %v", desc.Users["jch"].Password.Type)
|
||||
}
|
||||
|
||||
resp, err = do("DELETE", "/skald-api/v0/.groups/test/.users/jch",
|
||||
resp, err = do("DELETE", "/skald-api/v0/.halls/test/.users/jch",
|
||||
"", "", "", "")
|
||||
if err != nil || resp.StatusCode != http.StatusNoContent {
|
||||
t.Errorf("Delete group: %v %v", err, resp.StatusCode)
|
||||
t.Errorf("Delete hall: %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
desc, err = group.GetDescription("test")
|
||||
@@ -260,14 +260,14 @@ func TestApi(t *testing.T) {
|
||||
t.Errorf("Users (after delete): %#v", desc.Users)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/skald-api/v0/.groups/test/.wildcard-user",
|
||||
resp, err = do("PUT", "/skald-api/v0/.halls/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("/skald-api/v0/.groups/test/.wildcard-user", &user)
|
||||
err = getJSON("/skald-api/v0/.halls/test/.wildcard-user", &user)
|
||||
if err != nil {
|
||||
t.Errorf("Get wildcard user: %v", err)
|
||||
}
|
||||
@@ -281,7 +281,7 @@ func TestApi(t *testing.T) {
|
||||
t.Errorf("Got %v, expected %v", desc.WildcardUser, user)
|
||||
}
|
||||
|
||||
resp, err = do("DELETE", "/skald-api/v0/.groups/test/.wildcard-user",
|
||||
resp, err = do("DELETE", "/skald-api/v0/.halls/test/.wildcard-user",
|
||||
"", "", "", "")
|
||||
if err != nil || resp.StatusCode != http.StatusNoContent {
|
||||
t.Errorf("Delete wildcard user: %v %v", err, resp.StatusCode)
|
||||
@@ -300,33 +300,33 @@ func TestApi(t *testing.T) {
|
||||
t.Errorf("Keys: %v", len(desc.AuthKeys))
|
||||
}
|
||||
|
||||
resp, err = do("POST", "/skald-api/v0/.groups/test/.tokens/",
|
||||
resp, err = do("POST", "/skald-api/v0/.halls/test/.tokens/",
|
||||
"application/json", "", "", `{"group":"bad"}`)
|
||||
if err != nil || resp.StatusCode != http.StatusBadRequest {
|
||||
t.Errorf("Create token (bad group): %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
resp, err = do("POST", "/skald-api/v0/.groups/test/.tokens/",
|
||||
resp, err = do("POST", "/skald-api/v0/.halls/test/.tokens/",
|
||||
"application/json", "", "", "{}")
|
||||
if err != nil || resp.StatusCode != http.StatusCreated {
|
||||
t.Errorf("Create token: %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
resp, err = do("POST", "/skald-api/v0/.groups/.tokens/",
|
||||
resp, err = do("POST", "/skald-api/v0/.halls/.tokens/",
|
||||
"application/json", "", "", "{\"includeSubgroups\": true}")
|
||||
if err != nil || resp.StatusCode != http.StatusCreated {
|
||||
t.Errorf("Create global token: %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
var toknames []string
|
||||
err = getJSON("/skald-api/v0/.groups/test/.tokens/", &toknames)
|
||||
err = getJSON("/skald-api/v0/.halls/test/.tokens/", &toknames)
|
||||
if err != nil || len(toknames) != 1 {
|
||||
t.Errorf("Get tokens: %v %v", err, toknames)
|
||||
}
|
||||
tokname := toknames[0]
|
||||
|
||||
var globalToknames []string
|
||||
err = getJSON("/skald-api/v0/.groups/test/.tokens/", &globalToknames)
|
||||
err = getJSON("/skald-api/v0/.halls/test/.tokens/", &globalToknames)
|
||||
if err != nil || len(globalToknames) != 1 {
|
||||
t.Errorf("Get global tokens: %v %v", err, globalToknames)
|
||||
}
|
||||
@@ -336,7 +336,7 @@ func TestApi(t *testing.T) {
|
||||
t.Errorf("token.List: %v %v", tokens, err)
|
||||
}
|
||||
|
||||
tokenpath := "/skald-api/v0/.groups/test/.tokens/" + tokname
|
||||
tokenpath := "/skald-api/v0/.halls/test/.tokens/" + tokname
|
||||
var tok token.Stateful
|
||||
err = getJSON(tokenpath, &tok)
|
||||
if err != nil {
|
||||
@@ -375,13 +375,13 @@ func TestApi(t *testing.T) {
|
||||
t.Errorf("Got %v, expected %v (%v)", tok.Expires, e, err)
|
||||
}
|
||||
|
||||
resp, err = do("PUT", "/skald-api/v0/.groups/test2",
|
||||
resp, err = do("PUT", "/skald-api/v0/.halls/test2",
|
||||
"application/json", "", "*", "{}")
|
||||
if err != nil || resp.StatusCode != http.StatusCreated {
|
||||
t.Errorf("Create test2: %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
tokenpath2 := "/skald-api/v0/.groups/test2/.tokens/" + tokname
|
||||
tokenpath2 := "/skald-api/v0/.halls/test2/.tokens/" + tokname
|
||||
resp, err = do("GET", tokenpath2, "", "", "", "")
|
||||
if err != nil || resp.StatusCode != http.StatusNotFound {
|
||||
t.Errorf("Get token in bad group: %v %v", err, resp.StatusCode)
|
||||
@@ -402,16 +402,16 @@ func TestApi(t *testing.T) {
|
||||
t.Errorf("Token list: %v %v", tokens, err)
|
||||
}
|
||||
|
||||
resp, err = do("DELETE", "/skald-api/v0/.groups/test/.keys",
|
||||
resp, err = do("DELETE", "/skald-api/v0/.halls/test/.keys",
|
||||
"", "", "", "")
|
||||
if err != nil || resp.StatusCode != http.StatusNoContent {
|
||||
t.Errorf("Delete keys: %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
resp, err = do("DELETE", "/skald-api/v0/.groups/test/",
|
||||
resp, err = do("DELETE", "/skald-api/v0/.halls/test/",
|
||||
"", "", "", "")
|
||||
if err != nil || resp.StatusCode != http.StatusNoContent {
|
||||
t.Errorf("Delete group: %v %v", err, resp.StatusCode)
|
||||
t.Errorf("Delete hall: %v %v", err, resp.StatusCode)
|
||||
}
|
||||
|
||||
_, err = group.GetDescription("test")
|
||||
@@ -448,8 +448,8 @@ func TestApiBadAuth(t *testing.T) {
|
||||
}
|
||||
|
||||
do("GET", "/skald-api/v0/.stats")
|
||||
do("GET", "/skald-api/v0/.groups/")
|
||||
do("PUT", "/skald-api/v0/.groups/test/")
|
||||
do("GET", "/skald-api/v0/.halls/")
|
||||
do("PUT", "/skald-api/v0/.halls/test/")
|
||||
|
||||
f, err := os.Create(filepath.Join(group.Directory, "test.json"))
|
||||
if err != nil {
|
||||
@@ -460,20 +460,20 @@ func TestApiBadAuth(t *testing.T) {
|
||||
}\n`)
|
||||
f.Close()
|
||||
|
||||
do("PUT", "/skald-api/v0/.groups/test/")
|
||||
do("DELETE", "/skald-api/v0/.groups/test/")
|
||||
do("GET", "/skald-api/v0/.groups/test/.users/")
|
||||
do("GET", "/skald-api/v0/.groups/test/.users/jch")
|
||||
do("GET", "/skald-api/v0/.groups/test/.users/jch")
|
||||
do("PUT", "/skald-api/v0/.groups/test/.users/jch")
|
||||
do("DELETE", "/skald-api/v0/.groups/test/.users/jch")
|
||||
do("GET", "/skald-api/v0/.groups/test/.users/not-jch")
|
||||
do("PUT", "/skald-api/v0/.groups/test/.users/not-jch")
|
||||
do("PUT", "/skald-api/v0/.groups/test/.users/jch/.password")
|
||||
do("POST", "/skald-api/v0/.groups/test/.users/jch/.password")
|
||||
do("GET", "/skald-api/v0/.groups/test/.tokens/")
|
||||
do("POST", "/skald-api/v0/.groups/test/.tokens/")
|
||||
do("GET", "/skald-api/v0/.groups/test/.tokens/token")
|
||||
do("PUT", "/skald-api/v0/.groups/test/.tokens/token")
|
||||
do("DELETE", "/skald-api/v0/.groups/test/.tokens/token")
|
||||
do("PUT", "/skald-api/v0/.halls/test/")
|
||||
do("DELETE", "/skald-api/v0/.halls/test/")
|
||||
do("GET", "/skald-api/v0/.halls/test/.users/")
|
||||
do("GET", "/skald-api/v0/.halls/test/.users/jch")
|
||||
do("GET", "/skald-api/v0/.halls/test/.users/jch")
|
||||
do("PUT", "/skald-api/v0/.halls/test/.users/jch")
|
||||
do("DELETE", "/skald-api/v0/.halls/test/.users/jch")
|
||||
do("GET", "/skald-api/v0/.halls/test/.users/not-jch")
|
||||
do("PUT", "/skald-api/v0/.halls/test/.users/not-jch")
|
||||
do("PUT", "/skald-api/v0/.halls/test/.users/jch/.password")
|
||||
do("POST", "/skald-api/v0/.halls/test/.users/jch/.password")
|
||||
do("GET", "/skald-api/v0/.halls/test/.tokens/")
|
||||
do("POST", "/skald-api/v0/.halls/test/.tokens/")
|
||||
do("GET", "/skald-api/v0/.halls/test/.tokens/token")
|
||||
do("PUT", "/skald-api/v0/.halls/test/.tokens/token")
|
||||
do("DELETE", "/skald-api/v0/.halls/test/.tokens/token")
|
||||
}
|
||||
|
||||
@@ -35,7 +35,7 @@ var Insecure bool
|
||||
|
||||
func Serve(address string, dataDir string) error {
|
||||
http.Handle("/", &fileHandler{http.Dir(StaticRoot)})
|
||||
http.HandleFunc("/group/", groupHandler)
|
||||
http.HandleFunc("/hall/", groupHandler)
|
||||
http.HandleFunc("/recordings",
|
||||
func(w http.ResponseWriter, r *http.Request) {
|
||||
http.Redirect(w, r,
|
||||
@@ -43,7 +43,7 @@ func Serve(address string, dataDir string) error {
|
||||
})
|
||||
http.HandleFunc("/recordings/", recordingsHandler)
|
||||
http.HandleFunc("/ws", wsHandler)
|
||||
http.HandleFunc("/public-groups.json", publicHandler)
|
||||
http.HandleFunc("/public-halls.json", publicHandler)
|
||||
http.HandleFunc("/skald-api/", apiHandler)
|
||||
|
||||
s := &http.Server{
|
||||
@@ -346,7 +346,7 @@ func groupHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
name := parseGroupName("/group/", r.URL.Path)
|
||||
name := parseGroupName("/hall/", r.URL.Path)
|
||||
if name == "" {
|
||||
notFound(w)
|
||||
return
|
||||
@@ -358,8 +358,8 @@ func groupHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if r.URL.Path != "/group/"+name+"/" {
|
||||
http.Redirect(w, r, "/group/"+name+"/",
|
||||
if r.URL.Path != "/hall/"+name+"/" {
|
||||
http.Redirect(w, r, "/hall/"+name+"/",
|
||||
http.StatusPermanentRedirect)
|
||||
return
|
||||
}
|
||||
@@ -415,7 +415,7 @@ func groupStatusHandler(w http.ResponseWriter, r *http.Request) {
|
||||
internalError(w, "groupStatusHandler: this shouldn't happen")
|
||||
return
|
||||
}
|
||||
name := parseGroupName("/group/", pth)
|
||||
name := parseGroupName("/hall/", pth)
|
||||
if name == "" {
|
||||
notFound(w)
|
||||
return
|
||||
@@ -705,13 +705,13 @@ func handleGroupAction(w http.ResponseWriter, r *http.Request, group string) {
|
||||
}
|
||||
}
|
||||
|
||||
func checkGroupPermissions(w http.ResponseWriter, r *http.Request, groupname string) bool {
|
||||
func checkGroupPermissions(w http.ResponseWriter, r *http.Request, hallname string) bool {
|
||||
user, pass, ok := r.BasicAuth()
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
g := group.Get(groupname)
|
||||
g := group.Get(hallname)
|
||||
if g == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -19,17 +19,17 @@ func TestParseGroupName(t *testing.T) {
|
||||
{"/foo", ""},
|
||||
{"foo", ""},
|
||||
{"group/foo", ""},
|
||||
{"/group", ""},
|
||||
{"/group/..", ""},
|
||||
{"/group/foo/../bar", "bar"},
|
||||
{"/group/foo", "foo"},
|
||||
{"/group/foo/", "foo"},
|
||||
{"/group/foo/bar", "foo/bar"},
|
||||
{"/group/foo/bar/", "foo/bar"},
|
||||
{"/hall", ""},
|
||||
{"/hall/..", ""},
|
||||
{"/hall/foo/../bar", "bar"},
|
||||
{"/hall/foo", "foo"},
|
||||
{"/hall/foo/", "foo"},
|
||||
{"/hall/foo/bar", "foo/bar"},
|
||||
{"/hall/foo/bar/", "foo/bar"},
|
||||
}
|
||||
|
||||
for _, pg := range a {
|
||||
g := parseGroupName("/group/", pg.p)
|
||||
g := parseGroupName("/hall/", pg.p)
|
||||
if g != pg.g {
|
||||
t.Errorf("Path %v, got %v, expected %v",
|
||||
pg.p, g, pg.g)
|
||||
|
||||
+2
-2
@@ -153,7 +153,7 @@ func whipEndpointHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
name := parseGroupName("/group/", pth)
|
||||
name := parseGroupName("/hall/", pth)
|
||||
if name == "" {
|
||||
notFound(w)
|
||||
return
|
||||
@@ -269,7 +269,7 @@ func whipResourceHandler(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
name := parseGroupName("/group/", pth)
|
||||
name := parseGroupName("/hall/", pth)
|
||||
if name == "" {
|
||||
notFound(w)
|
||||
return
|
||||
|
||||
Reference in New Issue
Block a user