Skip to content

Commit 7609226

Browse files
authored
Various changes (#919)
* feat(config): deprecate a config param * refactor(config): order of struct declarations * feat(config): new env variable for anilist language * feat(backend): request romji titles * fix(backend): send romaji stuff to backend * fix(backend): use referenced variables * feat(backend): respect the language title * fix(frontend): allow deleting users * fix(frontend): change default message shown * chore(backend): remove admin user registration * feat(backend,frontend): admin only action guard * feat(config): config param for admin access * refactor(backend): save into variable * feat(backend): new schema for registering users * feat(backend): respect new input param * fix(frontend): adapt to new gql schema * feat(frontend): allow creating user even if registration disabled * feat(backend): allow editing other user accounts * fix(frontend): adapt to new gql schema * feat(backend): allow changing `lot` of user * feat(database): migration to allow disabling users * feat(frontend,backend): do not allow disabled accounts to login * feat(backend): allow disabling users * feat(frontend): add btn to edit user * feat(frontend): allow editing users * refactor(frontend): use new hook for submit
1 parent 18e12c8 commit 7609226

26 files changed

+450
-208
lines changed

apps/backend/src/entities/user.rs

+1
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ pub struct Model {
2929
pub created_on: DateTimeUtc,
3030
pub is_demo: Option<bool>,
3131
pub lot: UserLot,
32+
pub is_disabled: Option<bool>,
3233
#[graphql(skip)]
3334
pub preferences: UserPreferences,
3435
#[graphql(skip)]

apps/backend/src/miscellaneous/resolver.rs

+60-34
Original file line numberDiff line numberDiff line change
@@ -216,8 +216,6 @@ struct PasswordUserInput {
216216
username: String,
217217
#[graphql(secret)]
218218
password: String,
219-
/// If provided, that means an admin tried to create an account.
220-
creator_user_id: Option<String>,
221219
}
222220

223221
#[derive(Debug, InputObject, Serialize, Deserialize, Clone)]
@@ -233,13 +231,11 @@ enum AuthUserInput {
233231
Oidc(OidcUserInput),
234232
}
235233

236-
impl AuthUserInput {
237-
fn creator_user_id(&self) -> Option<String> {
238-
match self {
239-
Self::Password(p) => p.creator_user_id.clone(),
240-
_ => None,
241-
}
242-
}
234+
#[derive(Debug, InputObject)]
235+
struct RegisterUserInput {
236+
data: AuthUserInput,
237+
/// If registration is disabled, this can be used to override it.
238+
admin_access_token: Option<String>,
243239
}
244240

245241
#[derive(Enum, Clone, Debug, Copy, PartialEq, Eq)]
@@ -261,6 +257,7 @@ enum RegisterResult {
261257

262258
#[derive(Enum, Clone, Debug, Copy, PartialEq, Eq)]
263259
enum LoginErrorVariant {
260+
AccountDisabled,
264261
UsernameDoesNotExist,
265262
CredentialsMismatch,
266263
IncorrectProviderChosen,
@@ -284,10 +281,14 @@ enum LoginResult {
284281

285282
#[derive(Debug, InputObject)]
286283
struct UpdateUserInput {
287-
username: Option<String>,
284+
user_id: String,
285+
is_disabled: Option<bool>,
286+
lot: Option<UserLot>,
288287
#[graphql(secret)]
289288
password: Option<String>,
289+
username: Option<String>,
290290
extra_information: Option<serde_json::Value>,
291+
admin_access_token: Option<String>,
291292
}
292293

293294
#[derive(Debug, InputObject)]
@@ -1252,7 +1253,7 @@ impl MiscellaneousMutation {
12521253
async fn register_user(
12531254
&self,
12541255
gql_ctx: &Context<'_>,
1255-
input: AuthUserInput,
1256+
input: RegisterUserInput,
12561257
) -> Result<RegisterResult> {
12571258
let service = gql_ctx.data_unchecked::<Arc<MiscellaneousService>>();
12581259
service.register_user(input).await
@@ -1271,7 +1272,7 @@ impl MiscellaneousMutation {
12711272
input: UpdateUserInput,
12721273
) -> Result<StringIdObject> {
12731274
let service = gql_ctx.data_unchecked::<Arc<MiscellaneousService>>();
1274-
let user_id = self.user_id_from_ctx(gql_ctx).await?;
1275+
let user_id = self.user_id_from_ctx(gql_ctx).await.ok();
12751276
service.update_user(user_id, input).await
12761277
}
12771278

@@ -3715,9 +3716,13 @@ impl MiscellaneousService {
37153716
.await,
37163717
),
37173718
MediaSource::Tmdb => Box::new(self.get_tmdb_non_media_service().await?),
3718-
MediaSource::Anilist => {
3719-
Box::new(NonMediaAnilistService::new(self.config.frontend.page_size).await)
3720-
}
3719+
MediaSource::Anilist => Box::new(
3720+
NonMediaAnilistService::new(
3721+
&self.config.anime_and_manga.anilist,
3722+
self.config.frontend.page_size,
3723+
)
3724+
.await,
3725+
),
37213726
MediaSource::Mal => Box::new(NonMediaMalService::new().await),
37223727
MediaSource::Custom => return err(),
37233728
};
@@ -4854,33 +4859,33 @@ impl MiscellaneousService {
48544859
Ok(())
48554860
}
48564861

4857-
async fn register_user(&self, input: AuthUserInput) -> Result<RegisterResult> {
4858-
if let Some(user_id) = input.creator_user_id() {
4859-
self.admin_account_guard(&user_id).await?;
4860-
} else if !self.config.users.allow_registration {
4862+
async fn register_user(&self, input: RegisterUserInput) -> Result<RegisterResult> {
4863+
if !self.config.users.allow_registration
4864+
&& input.admin_access_token.unwrap_or_default() != self.config.server.admin_access_token
4865+
{
48614866
return Ok(RegisterResult::Error(RegisterError {
48624867
error: RegisterErrorVariant::Disabled,
48634868
}));
48644869
}
4865-
let (filter, username, password) = match input.clone() {
4866-
AuthUserInput::Oidc(input) => (
4867-
user::Column::OidcIssuerId.eq(&input.issuer_id),
4868-
input.email,
4870+
let (filter, username, password) = match input.data.clone() {
4871+
AuthUserInput::Oidc(data) => (
4872+
user::Column::OidcIssuerId.eq(&data.issuer_id),
4873+
data.email,
48694874
None,
48704875
),
4871-
AuthUserInput::Password(input) => (
4872-
user::Column::Name.eq(&input.username),
4873-
input.username,
4874-
Some(input.password),
4876+
AuthUserInput::Password(data) => (
4877+
user::Column::Name.eq(&data.username),
4878+
data.username,
4879+
Some(data.password),
48754880
),
48764881
};
48774882
if User::find().filter(filter).count(&self.db).await.unwrap() != 0 {
48784883
return Ok(RegisterResult::Error(RegisterError {
48794884
error: RegisterErrorVariant::IdentifierAlreadyExists,
48804885
}));
48814886
};
4882-
let oidc_issuer_id = match input {
4883-
AuthUserInput::Oidc(input) => Some(input.issuer_id),
4887+
let oidc_issuer_id = match input.data {
4888+
AuthUserInput::Oidc(data) => Some(data.issuer_id),
48844889
AuthUserInput::Password(_) => None,
48854890
};
48864891
let lot = if User::find().count(&self.db).await.unwrap() == 0 {
@@ -4914,6 +4919,11 @@ impl MiscellaneousService {
49144919
error: LoginErrorVariant::UsernameDoesNotExist,
49154920
})),
49164921
Some(user) => {
4922+
if user.is_disabled.unwrap_or_default() {
4923+
return Ok(LoginResult::Error(LoginError {
4924+
error: LoginErrorVariant::AccountDisabled,
4925+
}));
4926+
}
49174927
if self.config.users.validate_password {
49184928
if let AuthUserInput::Password(PasswordUserInput { password, .. }) = input {
49194929
if let Some(hashed_password) = user.password {
@@ -4958,8 +4968,17 @@ impl MiscellaneousService {
49584968
Ok(())
49594969
}
49604970

4961-
async fn update_user(&self, user_id: String, input: UpdateUserInput) -> Result<StringIdObject> {
4962-
let mut user_obj: user::ActiveModel = User::find_by_id(user_id.to_owned())
4971+
async fn update_user(
4972+
&self,
4973+
user_id: Option<String>,
4974+
input: UpdateUserInput,
4975+
) -> Result<StringIdObject> {
4976+
if user_id.unwrap_or_default() != input.user_id
4977+
&& input.admin_access_token.unwrap_or_default() != self.config.server.admin_access_token
4978+
{
4979+
return Err(Error::new("Admin access token mismatch".to_owned()));
4980+
}
4981+
let mut user_obj: user::ActiveModel = User::find_by_id(input.user_id)
49634982
.one(&self.db)
49644983
.await
49654984
.unwrap()
@@ -4974,6 +4993,12 @@ impl MiscellaneousService {
49744993
if let Some(i) = input.extra_information {
49754994
user_obj.extra_information = ActiveValue::Set(Some(i));
49764995
}
4996+
if let Some(l) = input.lot {
4997+
user_obj.lot = ActiveValue::Set(l);
4998+
}
4999+
if let Some(d) = input.is_disabled {
5000+
user_obj.is_disabled = ActiveValue::Set(Some(d));
5001+
}
49775002
let user_obj = user_obj.update(&self.db).await.unwrap();
49785003
Ok(StringIdObject { id: user_obj.id })
49795004
}
@@ -5791,19 +5816,20 @@ impl MiscellaneousService {
57915816
async fn admin_account_guard(&self, user_id: &String) -> Result<()> {
57925817
let main_user = user_by_id(&self.db, user_id).await?;
57935818
if main_user.lot != UserLot::Admin {
5794-
return Err(Error::new("Only admins can perform this operation."));
5819+
return Err(Error::new(BackendError::AdminOnlyAction.to_string()));
57955820
}
57965821
Ok(())
57975822
}
57985823

57995824
async fn users_list(&self, query: Option<String>) -> Result<Vec<user::Model>> {
5800-
Ok(User::find()
5825+
let users = User::find()
58015826
.apply_if(query, |query, value| {
58025827
query.filter(Expr::col(user::Column::Name).ilike(ilike_sql(&value)))
58035828
})
58045829
.order_by_asc(user::Column::Name)
58055830
.all(&self.db)
5806-
.await?)
5831+
.await?;
5832+
Ok(users)
58075833
}
58085834

58095835
async fn delete_user(&self, to_delete_user_id: String) -> Result<bool> {

apps/backend/src/models.rs

+2-1
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,10 @@ pub enum EntityLot {
5656
#[derive(Enum, Clone, Debug, Copy, PartialEq, Eq, Serialize, Deserialize, EnumIter, Display)]
5757
#[strum(serialize_all = "SCREAMING_SNAKE_CASE")]
5858
pub enum BackendError {
59-
NoAuthToken,
6059
NoUserId,
60+
NoAuthToken,
6161
SessionExpired,
62+
AdminOnlyAction,
6263
MutationNotAllowed,
6364
}
6465

apps/backend/src/providers/anilist/media_details.graphql

+2
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ query MediaDetailsQuery($id: Int!) {
44
title {
55
english
66
native
7+
romaji
78
}
89
isAdult
910
episodes
@@ -50,6 +51,7 @@ query MediaDetailsQuery($id: Int!) {
5051
title {
5152
english
5253
native
54+
romaji
5355
}
5456
coverImage {
5557
extraLarge

apps/backend/src/providers/anilist/media_search.graphql

+1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ query MediaSearchQuery(
1313
title {
1414
english
1515
native
16+
romaji
1617
}
1718
coverImage {
1819
extraLarge

0 commit comments

Comments
 (0)