Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 9 additions & 0 deletions backend/app/api/v1/auth/auth.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
login_service,
logout_service,
)
from app.core.config import settings
from app.core.mfa import (
setup_mfa_service,
validate_mfa_and_issue_token_service,
Expand Down Expand Up @@ -90,3 +91,11 @@ def get_providers():
Get a list of available external authentication providers.
"""
return get_external_auth_providers_service()


@router.get("/motd", response_model=MessageResponse)
def get_motd():
"""
Get the welcome message of the day.
"""
return MessageResponse(message=settings.WELCOME_MESSAGE or "")
3 changes: 3 additions & 0 deletions backend/app/core/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -52,6 +52,9 @@ class Settings(BaseSettings):
# External Identity Providers
EXTERNAL_AUTH_CONFIGS: list[ExternalAuthConfig] = []

# Welcome message
WELCOME_MESSAGE: str | None = None

# MITRE settings
MITRE_JSON_URL: str = "https://raw.githubusercontent.com/mitre/cti/master/enterprise-attack/enterprise-attack.json"

Expand Down
1 change: 1 addition & 0 deletions backend/app/schemas/configuration.py
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,5 @@ class Configuration(BaseModel):
MITRE_JSON_URL: str
CUSTOM_DATA_URL: str | None
ATOMIC_RED_TEAM_URL: str
WELCOME_MESSAGE: str | None
EXTERNAL_AUTH_CONFIGS: list[ExternalAuthConfig] | None
1 change: 1 addition & 0 deletions docs/docs/admin-guide/configuration.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ The in-app [configuration page](../user-guide/administration.md#view-system-conf
| `APPLICATION_NAME` | `RAPTR` | Name used for JWT tokens as `iss` and `aud` claims, as MFA issuer during TOTP setup and as logger name |
| `LOG_LEVEL` | `INFO` | Python logging level: `DEBUG`, `INFO`, `WARNING`, `ERROR`, `CRITICAL` |
| `FASTAPI_DOCUMENTATION` | `true` | Enable the OpenAPI documentation at `/docs` and `/redoc`. Set to `false` in production to hide the API docs |
| `WELCOME_MESSAGE` | *(none)* | Message displayed on the login page |

??? bug "Inconsistent Logging"
The implemented logging requires a rework. Not all relevant logs are created or stored in the DB (audit trail).
Expand Down
2 changes: 1 addition & 1 deletion docs/docs/user-guide/administration.md
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ The configuration page `/admin/configuration` displays all current system settin

Displayed settings include:

- **General**: application name, log level, admin email
- **General**: application name, log level, admin email, welcome message
- **Security**: minimum password length, OTP settings, JWT configuration, token expiry
- **Database**: PostgreSQL connection details
- **External Resources**: URLs for MITRE data, Atomic Red Team templates, and custom template repositories
Expand Down
2 changes: 1 addition & 1 deletion frontend/openapi.json

Large diffs are not rendered by default.

42 changes: 42 additions & 0 deletions frontend/src/types/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,26 @@ export interface paths {
patch?: never;
trace?: never;
};
"/api/v1/auth/motd": {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
/**
* Get Motd
* @description Get the welcome message of the day.
*/
get: operations["get_motd_api_v1_auth_motd_get"];
put?: never;
post?: never;
delete?: never;
options?: never;
head?: never;
patch?: never;
trace?: never;
};
"/api/v1/acl/": {
parameters: {
query?: never;
Expand Down Expand Up @@ -2832,6 +2852,8 @@ export interface components {
CUSTOM_DATA_URL: string | null;
/** Atomic Red Team Url */
ATOMIC_RED_TEAM_URL: string;
/** Welcome Message */
WELCOME_MESSAGE: string | null;
/** External Auth Configs */
EXTERNAL_AUTH_CONFIGS: components["schemas"]["ExternalAuthConfig"][] | null;
};
Expand Down Expand Up @@ -3878,6 +3900,26 @@ export interface operations {
};
};
};
get_motd_api_v1_auth_motd_get: {
parameters: {
query?: never;
header?: never;
path?: never;
cookie?: never;
};
requestBody?: never;
responses: {
/** @description Successful Response */
200: {
headers: {
[name: string]: unknown;
};
content: {
"application/json": components["schemas"]["MessageResponse"];
};
};
};
};
get_acls_api_v1_acl__get: {
parameters: {
query?: never;
Expand Down
9 changes: 9 additions & 0 deletions frontend/src/types/zod.ts
Original file line number Diff line number Diff line change
Expand Up @@ -320,6 +320,7 @@ const Configuration = z
MITRE_JSON_URL: z.string(),
CUSTOM_DATA_URL: z.union([z.string(), z.null()]),
ATOMIC_RED_TEAM_URL: z.string(),
WELCOME_MESSAGE: z.union([z.string(), z.null()]),
EXTERNAL_AUTH_CONFIGS: z.union([z.array(ExternalAuthConfig), z.null()]),
})
.passthrough();
Expand Down Expand Up @@ -3228,6 +3229,14 @@ Returns a provisioning URI for QR code generation.`,
},
],
},
{
method: "get",
path: "/api/v1/auth/motd",
alias: "get_motd_api_v1_auth_motd_get",
description: `Get the welcome message of the day.`,
requestFormat: "json",
response: z.object({ message: z.string() }).passthrough(),
},
{
method: "get",
path: "/api/v1/auth/providers",
Expand Down
23 changes: 21 additions & 2 deletions frontend/src/views/LoginView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { toTypedSchema } from '@vee-validate/zod';
import { useForm } from 'vee-validate';
import { onMounted, ref } from 'vue';
import { useRouter } from 'vue-router';
import { Info } from 'lucide-vue-next';
import { Button } from '@/components/ui/button';
import {
Card,
Expand All @@ -20,18 +21,31 @@ import {
FormMessage,
} from '@/components/ui/form';
import { Input } from '@/components/ui/input';
import { api } from '@/services/api';
import { useAuthStore } from '@/stores/auth';
import { schemas } from '@/types/zod';

const router = useRouter();
const authStore = useAuthStore();
const loading = ref(false);
const motd = ref<string | null>(null);

const usernameInput = ref<InstanceType<typeof Input> | null>(null);

onMounted(() => {
onMounted(async () => {
authStore.fetchProviders();
usernameInput.value?.$el?.focus();

try {
const response = await api.get<{ message: string | null }>(
'/auth/motd',
);
if (response.data.message) {
motd.value = response.data.message;
}
} catch (e) {
// Silently ignore if MOTD fetch fails
}
});

const formSchema = toTypedSchema(
Expand Down Expand Up @@ -68,7 +82,12 @@ const onSubmit = form.handleSubmit(async (values) => {
</script>

<template>
<div class="flex items-center justify-center min-h-screen p-4">
<div class="flex flex-col items-center justify-center min-h-screen p-4">
<div v-if="motd" class="w-full max-w-md mb-6 p-4 rounded-lg bg-blue-50 dark:bg-blue-950 border border-blue-200 dark:border-blue-900 text-blue-800 dark:text-blue-200 text-sm flex items-start shadow-sm">
<Info class="w-5 h-5 mr-3 shrink-0 mt-0.5" />
<div class="leading-relaxed whitespace-pre-wrap">{{ motd }}</div>
</div>

<Card class="w-full max-w-md">
<CardHeader class="space-y-1">
<CardTitle class="text-2xl font-bold text-center">Sign In</CardTitle>
Expand Down
4 changes: 4 additions & 0 deletions frontend/src/views/admin/ConfigurationView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@
<span class="text-sm font-medium">Admin User</span>
<span class="text-sm font-mono text-muted-foreground">{{ configuration.ADMIN_EMAIL }}</span>
</div>
<div class="grid grid-cols-2 items-center gap-4">
<span class="text-sm font-medium">Welcome Message</span>
<span class="text-sm font-mono text-muted-foreground whitespace-pre-wrap">{{ configuration.WELCOME_MESSAGE || 'None' }}</span>
</div>
</CardContent>
</Card>

Expand Down
Loading