forked from 2sys/phonograph
fix email uniqueness logic for new signups
This commit is contained in:
parent
fc8e3d6b99
commit
b701270d88
5 changed files with 60 additions and 20 deletions
|
|
@ -0,0 +1,2 @@
|
|||
drop index if exists users_email_idx;
|
||||
create index on users (email);
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
drop index if exists users_email_idx;
|
||||
create unique index on users (email);
|
||||
|
|
@ -110,29 +110,55 @@ returning id, uid, email
|
|||
.await?
|
||||
{
|
||||
user
|
||||
} else if uid.is_some() {
|
||||
// Conflict should have been on at least `uid`, meaning that the
|
||||
// user already exists and its fields are fully populated.
|
||||
query_as!(User, "select id, uid, email from users where uid = $1", uid)
|
||||
.fetch_one(app_db.get_conn())
|
||||
.await?
|
||||
} else {
|
||||
// Conflict must have been on `email`, meaning that the user
|
||||
// already exists, though its `uid` may not be up to date. Use
|
||||
// `COALESCE()` on the `uid` value to ensure that it is not
|
||||
// inadvertently removed if present.
|
||||
query_as!(
|
||||
// Conflict may have been on either or both of `email` and
|
||||
// `uid`.
|
||||
//
|
||||
// If the conflict was on `email`, we want to ensure that `uid`
|
||||
// is updated if and only if it is currently null. This can be
|
||||
// accomplished using `COALESCE()` with the current `uid` value
|
||||
// as the first argument. The parameterized value will only be
|
||||
// used if the current value is null.
|
||||
//
|
||||
// If the conflict was on `uid`, we merely want to return the
|
||||
// record with the matching `uid`.
|
||||
//
|
||||
// If both fields conflicted then the `UPDATE` query will be
|
||||
// sufficient, but if the operation is using a different email
|
||||
// address than the one already stored then we will need one
|
||||
// final `SELECT` query to guarantee that we obtain a result.
|
||||
// Assuming that the caller queries by `uid` prior to starting
|
||||
// the upsert, this last query should only be hit when two HTTP
|
||||
// requests are running the auth flow concurrently.
|
||||
if let Some(user) = query_as!(
|
||||
User,
|
||||
"
|
||||
update users
|
||||
set uid = coalesce($1, uid)
|
||||
where email = lower($1)
|
||||
set uid = coalesce(uid, $1)
|
||||
where email = lower($2)
|
||||
returning id, uid, email
|
||||
",
|
||||
uid,
|
||||
email,
|
||||
)
|
||||
.fetch_one(app_db.get_conn())
|
||||
.fetch_optional(app_db.get_conn())
|
||||
.await?
|
||||
{
|
||||
user
|
||||
} else {
|
||||
User::with_uid(
|
||||
// TODO: This should hold true *unless* the database is
|
||||
// "tampered with" by an outside actor (such as a
|
||||
// developer performing manual maintenance). While such
|
||||
// an edge case is beyond the scope of the server's
|
||||
// responsibilities, panicking may be considered an
|
||||
// overreaction.
|
||||
uid.expect("uid must be non-null to cause a database conflict"),
|
||||
)
|
||||
.fetch_optional(app_db)
|
||||
.await?
|
||||
.ok_or(sqlx::Error::RowNotFound)?
|
||||
}
|
||||
},
|
||||
)
|
||||
}
|
||||
|
|
|
|||
|
|
@ -7,6 +7,7 @@ use phono_backends::{escape_identifier, pg_database::PgDatabase, rolnames::ROLE_
|
|||
use phono_models::{
|
||||
accessors::{Accessor as _, Actor, workspace::WorkspaceAccessor},
|
||||
user::User,
|
||||
workspace_user_perm::WorkspaceMembership,
|
||||
};
|
||||
use serde::Deserialize;
|
||||
use sqlx::query;
|
||||
|
|
@ -84,7 +85,7 @@ pub(super) async fn post(
|
|||
.await?
|
||||
.datname;
|
||||
query(&format!(
|
||||
"grant connect on database {db_name_esc} to {rolname}",
|
||||
"grant connect on database {db_name_esc} to {rolname} with grant option",
|
||||
db_name_esc = escape_identifier(&db_name),
|
||||
rolname = escape_identifier(&format!(
|
||||
"{ROLE_PREFIX_USER}{user_id}",
|
||||
|
|
@ -93,6 +94,20 @@ pub(super) async fn post(
|
|||
))
|
||||
.execute(root_client.get_conn())
|
||||
.await?;
|
||||
query(&format!(
|
||||
"grant usage, create on schema phono to {rolname} with grant option",
|
||||
rolname = escape_identifier(&format!(
|
||||
"{ROLE_PREFIX_USER}{user_id}",
|
||||
user_id = target_user.id.simple()
|
||||
))
|
||||
))
|
||||
.execute(root_client.get_conn())
|
||||
.await?;
|
||||
WorkspaceMembership::upsert()
|
||||
.workspace_id(workspace_id)
|
||||
.user_id(target_user.id)
|
||||
.execute(&mut app_db)
|
||||
.await?;
|
||||
|
||||
Ok(navigator
|
||||
.workspace_page()
|
||||
|
|
|
|||
|
|
@ -33,10 +33,5 @@
|
|||
{% endfor %}
|
||||
</menu>
|
||||
</section>
|
||||
<section class="workspace-nav__section">
|
||||
<div class="workspace-nav__heading">
|
||||
<h1>Shared With Me</h1>
|
||||
</div>
|
||||
</section>
|
||||
</nav>
|
||||
{% endblock %}
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue