Compare commits
3 commits
36cb1d2043
...
d518cdf739
Author | SHA1 | Date | |
---|---|---|---|
d518cdf739 | |||
27846a4fe4 | |||
f766a2054b |
11 changed files with 52 additions and 23 deletions
|
@ -44,7 +44,7 @@ public class FediverseAuthController(
|
||||||
[ProducesResponseType<SingleUrlResponse>(statusCode: StatusCodes.Status200OK)]
|
[ProducesResponseType<SingleUrlResponse>(statusCode: StatusCodes.Status200OK)]
|
||||||
public async Task<IActionResult> GetFediverseUrlAsync(
|
public async Task<IActionResult> GetFediverseUrlAsync(
|
||||||
[FromQuery] string instance,
|
[FromQuery] string instance,
|
||||||
[FromQuery] bool forceRefresh = false
|
[FromQuery(Name = "force-refresh")] bool forceRefresh = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (instance.Any(c => c is '@' or ':' or '/') || !instance.Contains('.'))
|
if (instance.Any(c => c is '@' or ':' or '/') || !instance.Contains('.'))
|
||||||
|
@ -139,7 +139,7 @@ public class FediverseAuthController(
|
||||||
[Authorize("*")]
|
[Authorize("*")]
|
||||||
public async Task<IActionResult> AddFediverseAccountAsync(
|
public async Task<IActionResult> AddFediverseAccountAsync(
|
||||||
[FromQuery] string instance,
|
[FromQuery] string instance,
|
||||||
[FromQuery] bool forceRefresh = false
|
[FromQuery(Name = "force-refresh")] bool forceRefresh = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
if (instance.Any(c => c is '@' or ':' or '/') || !instance.Contains('.'))
|
if (instance.Any(c => c is '@' or ':' or '/') || !instance.Contains('.'))
|
||||||
|
|
|
@ -173,6 +173,9 @@ public class ReportsController(
|
||||||
public async Task<IActionResult> GetReportsAsync(
|
public async Task<IActionResult> GetReportsAsync(
|
||||||
[FromQuery] int? limit = null,
|
[FromQuery] int? limit = null,
|
||||||
[FromQuery] Snowflake? before = null,
|
[FromQuery] Snowflake? before = null,
|
||||||
|
[FromQuery] Snowflake? after = null,
|
||||||
|
[FromQuery(Name = "by-reporter")] Snowflake? byReporter = null,
|
||||||
|
[FromQuery(Name = "by-target")] Snowflake? byTarget = null,
|
||||||
[FromQuery(Name = "include-closed")] bool includeClosed = false
|
[FromQuery(Name = "include-closed")] bool includeClosed = false
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
|
@ -187,11 +190,21 @@ public class ReportsController(
|
||||||
IQueryable<Report> query = db
|
IQueryable<Report> query = db
|
||||||
.Reports.Include(r => r.Reporter)
|
.Reports.Include(r => r.Reporter)
|
||||||
.Include(r => r.TargetUser)
|
.Include(r => r.TargetUser)
|
||||||
.Include(r => r.TargetMember)
|
.Include(r => r.TargetMember);
|
||||||
.OrderByDescending(r => r.Id);
|
|
||||||
|
if (byTarget != null && await db.Users.AnyAsync(u => u.Id == byTarget.Value))
|
||||||
|
query = query.Where(r => r.TargetUserId == byTarget.Value);
|
||||||
|
|
||||||
|
if (byReporter != null && await db.Users.AnyAsync(u => u.Id == byReporter.Value))
|
||||||
|
query = query.Where(r => r.ReporterId == byReporter.Value);
|
||||||
|
|
||||||
if (before != null)
|
if (before != null)
|
||||||
query = query.Where(r => r.Id < before.Value);
|
query = query.Where(r => r.Id < before.Value).OrderByDescending(r => r.Id);
|
||||||
|
else if (after != null)
|
||||||
|
query = query.Where(r => r.Id > after.Value).OrderBy(r => r.Id);
|
||||||
|
else
|
||||||
|
query = query.OrderByDescending(r => r.Id);
|
||||||
|
|
||||||
if (!includeClosed)
|
if (!includeClosed)
|
||||||
query = query.Where(r => r.Status == ReportStatus.Open);
|
query = query.Where(r => r.Status == ReportStatus.Open);
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,7 @@ public record UserResponse(
|
||||||
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] Instant? LastActive,
|
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] Instant? LastActive,
|
||||||
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] Instant? LastSidReroll,
|
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] Instant? LastSidReroll,
|
||||||
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] string? Timezone,
|
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] string? Timezone,
|
||||||
|
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] bool? Suspended,
|
||||||
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] bool? Deleted
|
[property: JsonProperty(NullValueHandling = NullValueHandling.Ignore)] bool? Deleted
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -23,26 +23,30 @@ public class LimitMiddleware : IMiddleware
|
||||||
Endpoint? endpoint = ctx.GetEndpoint();
|
Endpoint? endpoint = ctx.GetEndpoint();
|
||||||
LimitAttribute? attribute = endpoint?.Metadata.GetMetadata<LimitAttribute>();
|
LimitAttribute? attribute = endpoint?.Metadata.GetMetadata<LimitAttribute>();
|
||||||
|
|
||||||
|
Token? token = ctx.GetToken();
|
||||||
|
|
||||||
if (attribute == null)
|
if (attribute == null)
|
||||||
{
|
{
|
||||||
await next(ctx);
|
// Check for authorize attribute
|
||||||
return;
|
// If it exists, and the user is deleted, throw an error.
|
||||||
}
|
|
||||||
|
|
||||||
Token? token = ctx.GetToken();
|
|
||||||
if (
|
if (
|
||||||
token?.User.Deleted == true
|
endpoint?.Metadata.GetMetadata<AuthorizeAttribute>() != null
|
||||||
&& (!attribute.UsableBySuspendedUsers || token.User.DeletedBy == null)
|
&& token?.User.Deleted == true
|
||||||
)
|
)
|
||||||
{
|
{
|
||||||
throw new ApiError.Forbidden("Deleted users cannot access this endpoint.");
|
throw new ApiError.Forbidden("Deleted users cannot access this endpoint.");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (attribute.RequireAdmin && token?.User.Role != UserRole.Admin)
|
await next(ctx);
|
||||||
{
|
return;
|
||||||
throw new ApiError.Forbidden("This endpoint can only be used by admins.");
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (token?.User.Deleted == true && !attribute.UsableBySuspendedUsers)
|
||||||
|
throw new ApiError.Forbidden("Deleted users cannot access this endpoint.");
|
||||||
|
|
||||||
|
if (attribute.RequireAdmin && token?.User.Role != UserRole.Admin)
|
||||||
|
throw new ApiError.Forbidden("This endpoint can only be used by admins.");
|
||||||
|
|
||||||
if (
|
if (
|
||||||
attribute.RequireModerator
|
attribute.RequireModerator
|
||||||
&& token?.User.Role is not (UserRole.Admin or UserRole.Moderator)
|
&& token?.User.Role is not (UserRole.Admin or UserRole.Moderator)
|
||||||
|
|
|
@ -21,7 +21,6 @@ using Newtonsoft.Json.Linq;
|
||||||
namespace Foxnouns.Backend.Services;
|
namespace Foxnouns.Backend.Services;
|
||||||
|
|
||||||
public class ModerationRendererService(
|
public class ModerationRendererService(
|
||||||
DatabaseContext db,
|
|
||||||
UserRendererService userRenderer,
|
UserRendererService userRenderer,
|
||||||
MemberRendererService memberRenderer
|
MemberRendererService memberRenderer
|
||||||
)
|
)
|
||||||
|
|
|
@ -115,6 +115,7 @@ public class UserRendererService(
|
||||||
tokenHidden ? user.LastActive : null,
|
tokenHidden ? user.LastActive : null,
|
||||||
tokenHidden ? user.LastSidReroll : null,
|
tokenHidden ? user.LastSidReroll : null,
|
||||||
tokenHidden ? user.Timezone ?? "<none>" : null,
|
tokenHidden ? user.Timezone ?? "<none>" : null,
|
||||||
|
tokenHidden ? user is { Deleted: true, DeletedBy: not null } : null,
|
||||||
tokenHidden ? user.Deleted : null
|
tokenHidden ? user.Deleted : null
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,6 +26,7 @@ export type MeUser = UserWithMembers & {
|
||||||
last_active: string;
|
last_active: string;
|
||||||
last_sid_reroll: string;
|
last_sid_reroll: string;
|
||||||
timezone: string;
|
timezone: string;
|
||||||
|
suspended: boolean;
|
||||||
deleted: boolean;
|
deleted: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -21,10 +21,17 @@
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
{#if user && user.deleted}
|
{#if user && user.deleted}
|
||||||
<div class="suspended-alert text-center py-3 mb-2 px-2">
|
<div class="deleted-alert text-center py-3 mb-2 px-2">
|
||||||
|
{#if user.suspended}
|
||||||
<strong>{$t("nav.suspended-account-hint")}</strong>
|
<strong>{$t("nav.suspended-account-hint")}</strong>
|
||||||
<br />
|
<br />
|
||||||
<a href="/contact">{$t("nav.appeal-suspension-link")}</a>
|
<a href="/contact">{$t("nav.appeal-suspension-link")}</a>
|
||||||
|
{:else}
|
||||||
|
<strong>{$t("nav.deleted-account-hint")}</strong>
|
||||||
|
<br />
|
||||||
|
<a href="/settings/reactivate">{$t("nav.reactivate-account-link")}</a> •
|
||||||
|
<a href="/contact">{$t("nav.delete-permanently-link")}</a>
|
||||||
|
{/if}
|
||||||
</div>
|
</div>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
@ -66,7 +73,7 @@
|
||||||
</Navbar>
|
</Navbar>
|
||||||
|
|
||||||
<style>
|
<style>
|
||||||
.suspended-alert {
|
.deleted-alert {
|
||||||
color: var(--bs-danger-text-emphasis);
|
color: var(--bs-danger-text-emphasis);
|
||||||
background-color: var(--bs-danger-bg-subtle);
|
background-color: var(--bs-danger-bg-subtle);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,7 +4,10 @@
|
||||||
"log-in": "Log in or sign up",
|
"log-in": "Log in or sign up",
|
||||||
"settings": "Settings",
|
"settings": "Settings",
|
||||||
"suspended-account-hint": "Your account has been suspended. Your profile has been hidden and you will not be able to change any settings.",
|
"suspended-account-hint": "Your account has been suspended. Your profile has been hidden and you will not be able to change any settings.",
|
||||||
"appeal-suspension-link": "I want to appeal"
|
"appeal-suspension-link": "I want to appeal",
|
||||||
|
"deleted-account-hint": "You have requested deletion of your account. If you want to reactivate it, click the link below.",
|
||||||
|
"reactivate-account-link": "Reactivate account",
|
||||||
|
"delete-permanently-link": "I want my account deleted permanently"
|
||||||
},
|
},
|
||||||
"avatar-tooltip": "Avatar for {{name}}",
|
"avatar-tooltip": "Avatar for {{name}}",
|
||||||
"profile": {
|
"profile": {
|
||||||
|
|
|
@ -65,7 +65,7 @@ export const actions = {
|
||||||
try {
|
try {
|
||||||
const resp = await apiRequest<{ url: string }>(
|
const resp = await apiRequest<{ url: string }>(
|
||||||
"GET",
|
"GET",
|
||||||
`/auth/fediverse?instance=${encodeURIComponent(instance)}&forceRefresh=true`,
|
`/auth/fediverse?instance=${encodeURIComponent(instance)}&force-refresh=true`,
|
||||||
{ fetch, isInternal: true },
|
{ fetch, isInternal: true },
|
||||||
);
|
);
|
||||||
redirect(303, resp.url);
|
redirect(303, resp.url);
|
||||||
|
|
|
@ -24,7 +24,7 @@ export const actions = {
|
||||||
|
|
||||||
const { url } = await apiRequest<{ url: string }>(
|
const { url } = await apiRequest<{ url: string }>(
|
||||||
"GET",
|
"GET",
|
||||||
`/auth/fediverse/add-account?instance=${encodeURIComponent(instance)}&forceRefresh=true`,
|
`/auth/fediverse/add-account?instance=${encodeURIComponent(instance)}&force-refresh=true`,
|
||||||
{
|
{
|
||||||
isInternal: true,
|
isInternal: true,
|
||||||
fetch,
|
fetch,
|
||||||
|
|
Loading…
Reference in a new issue