summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--docs/api/invitations.md39
-rw-r--r--src/event/handlers/stream/test/invite.rs14
-rw-r--r--src/invite/app.rs6
-rw-r--r--src/invite/handlers/accept/mod.rs8
-rw-r--r--src/invite/handlers/accept/test.rs9
-rw-r--r--src/user/history.rs1
6 files changed, 31 insertions, 46 deletions
diff --git a/docs/api/invitations.md b/docs/api/invitations.md
index 04e92c8..9b86b9e 100644
--- a/docs/api/invitations.md
+++ b/docs/api/invitations.md
@@ -39,8 +39,7 @@ The request must be an empty JSON object.
### Success
-This endpoint will respond with a status of
-`200 Okay` when successful. The body of the response will be a JSON object describing the new invitation:
+This endpoint will respond with a status of `200 Okay` when successful. The body of the response will be a JSON object describing the new invitation:
```json
{
@@ -58,8 +57,7 @@ The response will include the following fields:
| `issuer` | string | The user ID of the invitation's issuer. |
| `issued_at` | string | The timestamp from which the invitation will expire. |
-Clients and their operators are responsible for delivering the invitation to the invitee. Clients are strongly recommended to construct a URL for the invitation so that the invitee can take action on it easily. The included client supports URLs of the format
-`https://example.net/invite/:id` (with the `:id` placeholder substituted with the invitation's ID).
+Clients and their operators are responsible for delivering the invitation to the invitee. Clients are strongly recommended to construct a URL for the invitation so that the invitee can take action on it easily. The included client supports URLs of the format `https://example.net/invite/:id` (with the `:id` placeholder substituted with the invitation's ID).
## `GET /api/invite/:id`
@@ -75,8 +73,7 @@ This endpoint requires the following path parameter:
### On success
-This endpoint will respond with a status of
-`200 Okay` when successful. The body of the response will be a JSON object describing the invitation:
+This endpoint will respond with a status of `200 Okay` when successful. The body of the response will be a JSON object describing the invitation:
```json
{
@@ -96,13 +93,11 @@ The response will include the following fields:
| `issuer` | string | The name of the invitation's issuer. |
| `issued_at` | string | The timestamp from which the invitation will expire. |
-Clients should present the
-`issuer` to the user when presenting an invitation, so as to personalize the invitation and help them understand their connection with the service.
+Clients should present the `issuer` to the user when presenting an invitation, so as to personalize the invitation and help them understand their connection with the service.
### Invitation not found
-This endpoint will respond with a status of
-`404 Not Found` when the invitation ID either does not exist, or has already been accepted.
+This endpoint will respond with a status of `404 Not Found` when the invitation ID either does not exist, or has already been accepted.
## `POST /api/invite/:id`
@@ -146,29 +141,9 @@ The proposed `name` must be valid. The precise definition of valid is still up i
<!-- This prose is duplicated from authentication.md, with small changes for context. If you edit it here, edit it there, too. -->
-This endpoint will respond with a status of
-`200 Okay` when successful. The body of the response will be a JSON object describing the newly-created user:
-
-```json
-{
- "id": "Uabcd1234",
- "name": "Andrea"
-}
-```
-
-The response will include the following fields:
-
-| Field | Type | Description |
-| :----- | :----- | :------------------------------------------------------------------------------------------------------------------------------------------------- |
-| `id` | string | A unique identifier for the newly-created user. This can be used to associate the user with other events, or to make API calls targeting the user. |
-| `name` | string | The user's name. |
-
-The returned name may not be identical to the name requested, as the name will be converted to [normalization form C](http://www.unicode.org/reports/tr15/) automatically. The returned name will include this normalization; the service will use the normalized name elsewhere, and does not store the originally requested name.
-
-The provided password will also be converted to normalization form C. However, the normalized password is not returned to the client.
+This endpoint will respond with a status of `204 No Content` when successful.
-The response will include a `Set-Cookie` header for the
-`identity` cookie, providing the client with a newly-minted identity token associated with the login created for this request. See the [authentication](./authentication.md) section for details on how this cookie may be used.
+The response will include a `Set-Cookie` header for the `identity` cookie, providing the client with a newly-minted identity token associated with the login created for this request. See the [authentication](./authentication.md) section for details on how this cookie may be used.
The cookie will expire if it is not used regularly.
diff --git a/src/event/handlers/stream/test/invite.rs b/src/event/handlers/stream/test/invite.rs
index 01372ce..f2d6950 100644
--- a/src/event/handlers/stream/test/invite.rs
+++ b/src/event/handlers/stream/test/invite.rs
@@ -28,11 +28,16 @@ async fn accepting_invite() {
// Accept the invite
let (name, password) = fixtures::user::propose();
- let (joiner, _) = app
+ let joiner = app
.invites()
.accept(&invite.id, &name, &password, &fixtures::now())
.await
.expect("accepting an invite succeeds");
+ let (_, joiner) = app
+ .tokens()
+ .validate(&joiner, &fixtures::now())
+ .await
+ .expect("a newly-issued secret is valid");
// Expect a login created event
@@ -57,11 +62,16 @@ async fn previously_accepted_invite() {
// Accept the invite
let (name, password) = fixtures::user::propose();
- let (joiner, _) = app
+ let joiner = app
.invites()
.accept(&invite.id, &name, &password, &fixtures::now())
.await
.expect("accepting an invite succeeds");
+ let (_, joiner) = app
+ .tokens()
+ .validate(&joiner, &fixtures::now())
+ .await
+ .expect("a newly-issued secret is valid");
// Subscribe
diff --git a/src/invite/app.rs b/src/invite/app.rs
index a286a8f..14c1440 100644
--- a/src/invite/app.rs
+++ b/src/invite/app.rs
@@ -47,7 +47,7 @@ impl<'a> Invites<'a> {
name: &Name,
password: &Password,
accepted_at: &DateTime,
- ) -> Result<(User, Secret), AcceptError> {
+ ) -> Result<Secret, AcceptError> {
let create = Create::begin(name, password, accepted_at);
let mut tx = self.db.begin().await?;
@@ -74,9 +74,9 @@ impl<'a> Invites<'a> {
let secret = tx.tokens().issue(stored.user(), accepted_at).await?;
tx.commit().await?;
- let login = stored.publish(self.events);
+ let _ = stored.publish(self.events);
- Ok((login.as_created(), secret))
+ Ok(secret)
}
pub async fn expire(&self, relative_to: &DateTime) -> Result<(), sqlx::Error> {
diff --git a/src/invite/handlers/accept/mod.rs b/src/invite/handlers/accept/mod.rs
index 9fa4d6a..cdf385f 100644
--- a/src/invite/handlers/accept/mod.rs
+++ b/src/invite/handlers/accept/mod.rs
@@ -7,12 +7,12 @@ use axum::{
use crate::{
app::App,
clock::RequestedAt,
+ empty::Empty,
error::{Internal, NotFound},
invite::{app, handlers::PathInfo},
name::Name,
password::Password,
token::extract::IdentityCookie,
- user::User,
};
#[cfg(test)]
@@ -24,14 +24,14 @@ pub async fn handler(
identity: IdentityCookie,
Path(invite): Path<PathInfo>,
Json(request): Json<Request>,
-) -> Result<(IdentityCookie, Json<User>), Error> {
- let (login, secret) = app
+) -> Result<(IdentityCookie, Empty), Error> {
+ let secret = app
.invites()
.accept(&invite, &request.name, &request.password, &accepted_at)
.await
.map_err(Error)?;
let identity = identity.set(secret);
- Ok((identity, Json(login)))
+ Ok((identity, Empty))
}
#[derive(serde::Deserialize)]
diff --git a/src/invite/handlers/accept/test.rs b/src/invite/handlers/accept/test.rs
index 7139985..adc7aa4 100644
--- a/src/invite/handlers/accept/test.rs
+++ b/src/invite/handlers/accept/test.rs
@@ -1,6 +1,6 @@
use axum::extract::{Json, Path, State};
-use crate::{invite::app::AcceptError, name::Name, test::fixtures};
+use crate::{empty::Empty, invite::app::AcceptError, name::Name, test::fixtures};
#[tokio::test]
async fn valid_invite() {
@@ -18,7 +18,7 @@ async fn valid_invite() {
name: name.clone(),
password: password.clone(),
};
- let (identity, Json(response)) = super::handler(
+ let (identity, Empty) = super::handler(
State(app.clone()),
fixtures::now(),
identity,
@@ -31,7 +31,6 @@ async fn valid_invite() {
// Verify the response
assert!(identity.secret().is_some());
- assert_eq!(name, response.name);
// Verify that the issued token is valid
@@ -43,7 +42,7 @@ async fn valid_invite() {
.validate(&secret, &fixtures::now())
.await
.expect("newly-issued identity cookie is valid");
- assert_eq!(response, login);
+ assert_eq!(name, login.name);
// Verify that the given credentials can log in
@@ -57,7 +56,7 @@ async fn valid_invite() {
.validate(&secret, &fixtures::now())
.await
.expect("validating a newly-issued token secret succeeds");
- assert_eq!(response, login);
+ assert_eq!(name, login.name);
}
#[tokio::test]
diff --git a/src/user/history.rs b/src/user/history.rs
index 72e0aee..4f99130 100644
--- a/src/user/history.rs
+++ b/src/user/history.rs
@@ -20,6 +20,7 @@ impl History {
// if this returns a redacted or modified version of the user. If we implement
// renames by redacting the original name, then this should return the edited
// user, not the original, even if that's not how it was "as created.")
+ #[cfg(test)]
pub fn as_created(&self) -> User {
self.user.clone()
}