Merge branch 'main' into PMPR-27
This commit is contained in:
@@ -1,6 +1,6 @@
|
||||
import { FreshContext } from "$fresh/server.ts";
|
||||
import { Partial } from "$fresh/runtime.ts";
|
||||
import { State } from "$root/routes/_middleware.ts";
|
||||
import { State } from "$root/defaults/interfaces.ts";
|
||||
import { AppProperties } from "$root/defaults/interfaces.ts";
|
||||
import Navbar from "$root/routes/(_islands)/Navbar.tsx";
|
||||
|
||||
|
||||
@@ -7,7 +7,6 @@ const properties: AppProperties = {
|
||||
index: "Homepage",
|
||||
notes: "Notes",
|
||||
courses: "Courses management",
|
||||
students: "Students management",
|
||||
},
|
||||
adminOnly: ["courses", "students"],
|
||||
hint: "Student grading management",
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { Partial } from "$fresh/runtime.ts";
|
||||
import { RouteConfig } from "$fresh/server.ts";
|
||||
import {
|
||||
getPartialsConfig,
|
||||
makePartials,
|
||||
} from "$root/defaults/makePartials.tsx";
|
||||
import { FreshContext } from "$fresh/server.ts";
|
||||
import { State } from "$root/routes/_middleware.ts";
|
||||
|
||||
type ModulesProps = Record<string | number | symbol, never>;
|
||||
|
||||
export const config: RouteConfig = {
|
||||
skipAppWrapper: true,
|
||||
skipInheritedLayouts: true,
|
||||
};
|
||||
|
||||
export default function Modules(_props: ModulesProps) {
|
||||
return (
|
||||
<Partial name="body">
|
||||
<a href="notes" f-partial={"notes/partials"}>notes</a>
|
||||
</Partial>
|
||||
);
|
||||
// deno-lint-ignore require-await
|
||||
async function Courses(_request: Request, context: FreshContext<State>) {
|
||||
return <h2>Welcome to {context.state.session?.displayName}.</h2>;
|
||||
}
|
||||
|
||||
export const config = getPartialsConfig();
|
||||
export default makePartials(Courses);
|
||||
|
||||
@@ -1,17 +0,0 @@
|
||||
import { Partial } from "$fresh/runtime.ts";
|
||||
import { RouteConfig } from "$fresh/server.ts";
|
||||
|
||||
type ModulesProps = Record<string | number | symbol, never>;
|
||||
|
||||
export const config: RouteConfig = {
|
||||
skipAppWrapper: true,
|
||||
skipInheritedLayouts: true,
|
||||
};
|
||||
|
||||
export default function Modules(_props: ModulesProps) {
|
||||
return (
|
||||
<Partial name="body">
|
||||
<a href="notes" f-partial={"notes/partials"}>notes</a>
|
||||
</Partial>
|
||||
);
|
||||
}
|
||||
@@ -2,11 +2,12 @@ import {
|
||||
getPartialsConfig,
|
||||
makePartials,
|
||||
} from "$root/defaults/makePartials.tsx";
|
||||
import { FreshContext } from "$fresh/server.ts";
|
||||
import { State } from "$root/routes/_middleware.ts";
|
||||
|
||||
type NotesIndexProps = Record<string | number | symbol, never>;
|
||||
|
||||
export function Index(_props: NotesIndexProps) {
|
||||
return <a href="notes" f-partial={"notes/partials"}>bip boup</a>;
|
||||
// deno-lint-ignore require-await
|
||||
export async function Index(_request: Request, context: FreshContext<State>) {
|
||||
return <h2>Welcome to {context.state.session?.displayName}.</h2>;
|
||||
}
|
||||
|
||||
export const config = getPartialsConfig();
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
import { Partial } from "$fresh/runtime.ts";
|
||||
import { RouteConfig } from "$fresh/server.ts";
|
||||
import {
|
||||
getPartialsConfig,
|
||||
makePartials,
|
||||
} from "$root/defaults/makePartials.tsx";
|
||||
import { FreshContext } from "$fresh/server.ts";
|
||||
import { State } from "$root/routes/_middleware.ts";
|
||||
|
||||
type ModulesProps = Record<string | number | symbol, never>;
|
||||
|
||||
export const config: RouteConfig = {
|
||||
skipAppWrapper: true,
|
||||
skipInheritedLayouts: true,
|
||||
};
|
||||
|
||||
export default function Modules(_props: ModulesProps) {
|
||||
return (
|
||||
<Partial name="body">
|
||||
<a href="notes" f-partial={"notes/partials"}>notes</a>
|
||||
</Partial>
|
||||
);
|
||||
// deno-lint-ignore require-await
|
||||
async function Notes(_request: Request, context: FreshContext<State>) {
|
||||
return <h2>Welcome to {context.state.session?.displayName}.</h2>;
|
||||
}
|
||||
|
||||
export const config = getPartialsConfig();
|
||||
export default makePartials(Notes);
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
import { FreshContext } from "$fresh/server.ts";
|
||||
import { State } from "$root/routes/_middleware.ts";
|
||||
import { State } from "$root/defaults/interfaces.ts";
|
||||
import Header from "$root/routes/(_components)/Header.tsx";
|
||||
import Footer from "$root/routes/(_components)/Footer.tsx";
|
||||
|
||||
|
||||
+38
-15
@@ -1,7 +1,7 @@
|
||||
import { FreshContext } from "$fresh/server.ts";
|
||||
import { FreshContext, MiddlewareHandler } from "$fresh/server.ts";
|
||||
import { getCookies } from "$std/http/cookie.ts";
|
||||
import { getJwtPayload, isJwtValid } from "@popov/jwt";
|
||||
import { CasContent, LoginJWT } from "$root/defaults/interfaces.ts";
|
||||
import { CasContent, LoginJWT, State } from "$root/defaults/interfaces.ts";
|
||||
|
||||
const PUBLIC_ROUTES = [
|
||||
"/",
|
||||
@@ -13,31 +13,45 @@ const PUBLIC_ROUTES = [
|
||||
];
|
||||
|
||||
const jwtKeyCache: Record<string, string> = {};
|
||||
const deleteKey = (user: string) => delete jwtKeyCache[user];
|
||||
|
||||
export interface State {
|
||||
isAuthenticated: boolean;
|
||||
session: CasContent;
|
||||
}
|
||||
|
||||
function isRoutePublic(route: string) {
|
||||
return PUBLIC_ROUTES.includes(route) || route.match(/\..+$/);
|
||||
/**
|
||||
* Checks if the given route is public.
|
||||
* @param route The route to check.
|
||||
* @returns `true` if the route is public, `false` otherwise.
|
||||
*/
|
||||
function isRoutePublic(route: string): boolean {
|
||||
return PUBLIC_ROUTES.includes(route) ||
|
||||
!!(route.match(/\..+$/)?.[0] ?? false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the given user's key, creating it if not already existing.
|
||||
* @param user The key's user.
|
||||
* @returns The user's key.
|
||||
*/
|
||||
export function getKey(user: string): string {
|
||||
if (!jwtKeyCache[user]) {
|
||||
const keyBuffer = new Uint8Array(32);
|
||||
crypto.getRandomValues(keyBuffer);
|
||||
jwtKeyCache[user] = new TextDecoder().decode(keyBuffer);
|
||||
setTimeout(deleteKey, 0x75300, user);
|
||||
}
|
||||
|
||||
return jwtKeyCache[user];
|
||||
}
|
||||
|
||||
export const handler = [
|
||||
export const handler: MiddlewareHandler<State>[] = [
|
||||
/**
|
||||
* Check if user is authenticated and add session to context accordingly.
|
||||
* @param request The HTTP incomming request.
|
||||
* @param context The Fresh context object with custom `State`.
|
||||
* @returns The response from the next middleware.
|
||||
*/
|
||||
async function checkAuthentication(
|
||||
request: Request,
|
||||
context: FreshContext<State>,
|
||||
) {
|
||||
): Promise<Response> {
|
||||
const cookies = getCookies(request.headers);
|
||||
if (!cookies["sessionToken"]) {
|
||||
context.state.isAuthenticated = false;
|
||||
@@ -51,17 +65,26 @@ export const handler = [
|
||||
cookies["sessionToken"],
|
||||
key,
|
||||
);
|
||||
const session: CasContent =
|
||||
(getJwtPayload(cookies["sessionToken"]) as LoginJWT).user;
|
||||
|
||||
context.state.session = session;
|
||||
if (context.state.isAuthenticated) {
|
||||
const session: CasContent =
|
||||
(getJwtPayload(cookies["sessionToken"]) as LoginJWT).user;
|
||||
context.state.session = session;
|
||||
}
|
||||
|
||||
return await context.next();
|
||||
},
|
||||
/**
|
||||
* Check if page can be accessed with or without authentication.
|
||||
* Redirect if the page is private and the user isn't authenticated.
|
||||
* @param request The HTTP incomming request.
|
||||
* @param context The Fresh context object with `State` set up.
|
||||
* @returns The response from the next middleware or from the page.
|
||||
*/
|
||||
async function ensureAuthentication(
|
||||
request: Request,
|
||||
context: FreshContext<State>,
|
||||
) {
|
||||
): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
|
||||
if (!isRoutePublic(url.pathname) && !context.state.isAuthenticated) {
|
||||
|
||||
+163
-1
@@ -9,7 +9,169 @@ export default async function About(_request: Request, _context: FreshContext) {
|
||||
PolyMPR is born from the will to enhance Polytech INFO department's HR
|
||||
infrastructure.
|
||||
</p>
|
||||
<h3>Terms of Use</h3>
|
||||
<h2>Terms of Use</h2>
|
||||
<p>
|
||||
<em>
|
||||
Last updated: 21<sup>th</sup> Jan. 2025
|
||||
</em>
|
||||
</p>
|
||||
<p>
|
||||
By accessing and using this website through the Aix-Marseille University
|
||||
(AMU) Single Sign-On (SSO) authentication system, you agree to comply
|
||||
with these Terms and Conditions. Please read them carefully before
|
||||
proceeding.
|
||||
</p>
|
||||
<h3>1. Acceptance of Terms</h3>
|
||||
<p>By logging in with your AMU SSO credentials, you confirm that:</p>
|
||||
<ul>
|
||||
<li>
|
||||
You are an authorized user of Aix-Marseille University's SSO system.
|
||||
</li>
|
||||
<li>
|
||||
You agree to be bound by these Terms and Conditions, as well as any
|
||||
additional rules, policies, or guidelines applicable to the use of
|
||||
this website.
|
||||
</li>
|
||||
</ul>
|
||||
<p>
|
||||
If you do not agree with these terms, you are not authorized to access
|
||||
or use this website.
|
||||
</p>
|
||||
<h3>2. Eligibility</h3>
|
||||
<p>
|
||||
Access to this website is restricted to authorized individuals
|
||||
affiliated with Aix-Marseille University, such as students, faculty,
|
||||
staff, or others explicitly granted access. Unauthorized use is strictly
|
||||
prohibited and may result in suspension or termination of access.
|
||||
</p>
|
||||
<h3>3. Authentication Through AMU SSO</h3>
|
||||
<ul>
|
||||
<li>
|
||||
Authentication through AMU's SSO system is required to access this
|
||||
website.
|
||||
</li>
|
||||
<li>
|
||||
You are responsible for safeguarding your AMU SSO login credentials
|
||||
and ensuring they are not shared with others.
|
||||
</li>
|
||||
<li>
|
||||
If you suspect unauthorized use of your AMU SSO credentials, you must
|
||||
immediately notify Aix-Marseille University's IT services at{" "}
|
||||
<a href="https://dirnum.univ-amu.fr/fr">
|
||||
https://dirnum.univ-amu.fr/fr
|
||||
</a>.
|
||||
</li>
|
||||
</ul>
|
||||
<h3>4. Permitted Use</h3>
|
||||
<p>By accessing the website, you agree to:</p>
|
||||
<ul>
|
||||
<li>
|
||||
Use the website only for its intended academic, administrative, or
|
||||
research purposes.
|
||||
</li>
|
||||
<li>
|
||||
Refrain from engaging in any of the following prohibited activities:
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
Sharing your access credentials with unauthorized individuals.
|
||||
</li>
|
||||
<li>
|
||||
Misusing, modifying, or attempting to exploit the website's
|
||||
services.
|
||||
</li>
|
||||
<li>
|
||||
Uploading or distributing malware, offensive content, or any
|
||||
material that violates university policies or applicable laws.
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
<h3>5. Privacy and Data Protection</h3>
|
||||
<p>By using this website:</p>
|
||||
<ul>
|
||||
<li>
|
||||
You acknowledge that your personal data, including your AMU SSO login
|
||||
and activity on the website, may be collected, processed, and stored
|
||||
in accordance with Aix-Marseille University’s privacy policy and
|
||||
applicable data protection laws (e.g., GDPR).
|
||||
</li>
|
||||
<li>
|
||||
This data is used for authentication, and improving the website's
|
||||
services.
|
||||
</li>
|
||||
</ul>
|
||||
<h3>6. Intellectual Property</h3>
|
||||
<ul>
|
||||
<li>
|
||||
All content and materials provided on this website are the
|
||||
intellectual property of Aix-Marseille University or its licensors.
|
||||
</li>
|
||||
<li>
|
||||
You are granted a limited, non-transferable license to use the content
|
||||
for personal, academic, or research purposes. Any unauthorized use,
|
||||
reproduction, or distribution is strictly prohibited.
|
||||
</li>
|
||||
</ul>
|
||||
<h3>7. Termination of Access</h3>
|
||||
<ul>
|
||||
<li>
|
||||
Aix-Marseille University reserves the right to suspend or terminate
|
||||
your access without notice if:
|
||||
</li>
|
||||
<ul>
|
||||
<li>
|
||||
You violate these Terms and Conditions or university policies.
|
||||
</li>
|
||||
<li>
|
||||
Your AMU affiliation is revoked or your SSO account is deactivated.
|
||||
</li>
|
||||
</ul>
|
||||
<li>
|
||||
Unauthorized access attempts may be reported to the appropriate
|
||||
authorities.
|
||||
</li>
|
||||
</ul>
|
||||
<h3>8. Disclaimers and Limitations of Liability</h3>
|
||||
<ul>
|
||||
<li>
|
||||
The website and its content are provided "as is" and "as available"
|
||||
without any warranties, express or implied.
|
||||
</li>
|
||||
<li>
|
||||
Aix-Marseille University and the website administrators are not liable
|
||||
for:
|
||||
</li>
|
||||
<ul>
|
||||
<li>Interruptions in service, data loss, or technical issues.</li>
|
||||
<li>
|
||||
Any unauthorized use of your AMU SSO credentials resulting from your
|
||||
negligence.
|
||||
</li>
|
||||
</ul>
|
||||
</ul>
|
||||
<h3>9. Modifications to the Terms</h3>
|
||||
<ul>
|
||||
<li>
|
||||
Aix-Marseille University may update these Terms and Conditions
|
||||
periodically to reflect changes in laws, policies, or services.
|
||||
</li>
|
||||
<li>
|
||||
Your continued use of the website following any changes constitutes
|
||||
your acceptance of the updated terms.
|
||||
</li>
|
||||
</ul>
|
||||
<h3>10. Governing Law</h3>
|
||||
<p>
|
||||
These Terms and Conditions are governed by and construed in accordance
|
||||
with the laws of France and applicable EU regulations.
|
||||
</p>
|
||||
<h3>11. Contact Information</h3>
|
||||
<p>
|
||||
For questions or support, please contact:{" "}
|
||||
<a href="https://dirnum.univ-amu.fr/fr">
|
||||
Aix-Marseille University IT Services
|
||||
</a>.
|
||||
</p>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
+22
-5
@@ -1,10 +1,24 @@
|
||||
import { FreshContext, Handlers } from "$fresh/server.ts";
|
||||
import { AppProperties } from "$root/defaults/interfaces.ts";
|
||||
import { AppProperties, State } from "$root/defaults/interfaces.ts";
|
||||
import AppNavigator from "$root/routes/(_islands)/AppNavigator.tsx";
|
||||
|
||||
export const handler: Handlers = {
|
||||
async GET(_request, context) {
|
||||
const apps: Record<string, AppProperties> = {};
|
||||
const apps: Record<string, AppProperties> = {};
|
||||
|
||||
export const handler: Handlers<Record<string, AppProperties>, State> = {
|
||||
/**
|
||||
* Generate the app catalog page from pages.
|
||||
* Catalog is only computed once, then the cached version is used.
|
||||
* @param _request The HTTP incomming request.
|
||||
* @param context The Fresh context with `State`.
|
||||
* @returns The rendered page with all apps as catalog.
|
||||
*/
|
||||
async GET(
|
||||
_request: Request,
|
||||
context: FreshContext<State, Record<string, AppProperties>>,
|
||||
): Promise<Response> {
|
||||
if (Object.keys(apps).length != 0) {
|
||||
return context.render(apps);
|
||||
}
|
||||
|
||||
for await (const appDir of Deno.readDir("routes/(apps)")) {
|
||||
if (appDir.isFile) {
|
||||
@@ -26,7 +40,10 @@ export const handler: Handlers = {
|
||||
};
|
||||
|
||||
// deno-lint-ignore require-await
|
||||
export default async function Apps(_request: Request, context: FreshContext) {
|
||||
export default async function Apps(
|
||||
_request: Request,
|
||||
context: FreshContext<State, Record<string, AppProperties>>,
|
||||
) {
|
||||
return (
|
||||
<>
|
||||
<AppNavigator apps={context.data} />
|
||||
|
||||
+4
-1
@@ -4,7 +4,10 @@ import { FreshContext } from "$fresh/server.ts";
|
||||
export default async function Home(_request: Request, _context: FreshContext) {
|
||||
return (
|
||||
<>
|
||||
<h2>Welcome to PolyMPR!</h2>
|
||||
<h2>PolyMPR</h2>
|
||||
<h3>
|
||||
The <em>ultimate</em> HR platform
|
||||
</h3>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
+58
-50
@@ -1,5 +1,5 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { State } from "$root/routes/_middleware.ts";
|
||||
import { FreshContext, Handlers } from "$fresh/server.ts";
|
||||
import { State } from "$root/defaults/interfaces.ts";
|
||||
import { parse, type RegularTagNode } from "@melvdouc/xml-parser";
|
||||
import {
|
||||
CasContent,
|
||||
@@ -11,16 +11,25 @@ import { createJwt } from "@popov/jwt";
|
||||
import { setCookie } from "$std/http/cookie.ts";
|
||||
import { getKey } from "$root/routes/_middleware.ts";
|
||||
|
||||
const SERVICE = "https://localhost/login";
|
||||
const CAS = "https://ident.univ-amu.fr/cas";
|
||||
|
||||
function getTag(tag: CasTagNode): [string, string] {
|
||||
/**
|
||||
* Get the tag node value without "cas:" prefix in name.
|
||||
* @param tag The CAS tag node.
|
||||
* @returns The `[name, value]` pair.
|
||||
*/
|
||||
function getTag(tag: CasTagNode): [name: string, value: string] {
|
||||
return [
|
||||
tag.tagName.replace("cas:", ""),
|
||||
tag.children[0].value,
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the user JWT token with a validity period of one hour.
|
||||
* @param casResponse The CAS reponse parsed from XML.
|
||||
* @returns The user JWT session token.
|
||||
*/
|
||||
function createUserJWT(casResponse: CasResponse): Promise<string> {
|
||||
const nodes = casResponse.children[1].children.map(getTag);
|
||||
const fullUserInfos: Record<string, string | string[]> = {};
|
||||
@@ -29,7 +38,6 @@ function createUserJWT(casResponse: CasResponse): Promise<string> {
|
||||
if (typeof fullUserInfos[key] == "string") {
|
||||
fullUserInfos[key] = [fullUserInfos[key]];
|
||||
}
|
||||
|
||||
if (Array.isArray(fullUserInfos[key])) {
|
||||
fullUserInfos[key].push(value);
|
||||
} else {
|
||||
@@ -38,12 +46,10 @@ function createUserJWT(casResponse: CasResponse): Promise<string> {
|
||||
});
|
||||
|
||||
const now = Math.floor(Date.now() / 1000);
|
||||
const oneHour = 60 * 60;
|
||||
|
||||
const payload: LoginJWT = {
|
||||
iss: "PolyMPR",
|
||||
iat: now,
|
||||
exp: now + oneHour,
|
||||
exp: now + 0xe10,
|
||||
aud: "PolyMPR",
|
||||
user: fullUserInfos as unknown as CasContent,
|
||||
};
|
||||
@@ -52,56 +58,58 @@ function createUserJWT(casResponse: CasResponse): Promise<string> {
|
||||
return createJwt(payload, key);
|
||||
}
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export const handler: Handlers<any, State> = {
|
||||
async GET(request, context) {
|
||||
export const handler: Handlers<null, State> = {
|
||||
/**
|
||||
* Handles all CAS protocol requests.
|
||||
* @param request The incomming HTTP request.
|
||||
* @param context The Fresh context with `State`.
|
||||
* @returns The redirect corresponding to each step of the CAS protocol.
|
||||
*/
|
||||
async GET(
|
||||
request: Request,
|
||||
context: FreshContext<State, null>,
|
||||
): Promise<Response> {
|
||||
const url = new URL(request.url);
|
||||
const ticket = url.searchParams.get("ticket");
|
||||
const service = `${context.url.origin}/login`;
|
||||
|
||||
if (ticket) {
|
||||
const response = await fetch(
|
||||
`${CAS}/serviceValidate?service=${SERVICE}&ticket=${ticket}`,
|
||||
);
|
||||
const body = parse(await response.text()) as [RegularTagNode];
|
||||
const casResponse = body[0].children[0] as CasResponse;
|
||||
|
||||
if (casResponse.tagName != "cas:authenticationSuccess") {
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: {
|
||||
Location: `${CAS}/login?service=${SERVICE}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const headers = new Headers();
|
||||
|
||||
setCookie(headers, {
|
||||
name: "sessionToken",
|
||||
value: await createUserJWT(casResponse),
|
||||
});
|
||||
headers.set("Location", "/apps");
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers,
|
||||
});
|
||||
}
|
||||
|
||||
if (context.state.isAuthenticated) {
|
||||
if (!ticket) {
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: {
|
||||
Location: "/apps",
|
||||
},
|
||||
});
|
||||
} else {
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: {
|
||||
Location: `${CAS}/login?service=${SERVICE}`,
|
||||
Location: context.state.isAuthenticated
|
||||
? "/apps"
|
||||
: `${CAS}/login?service=${service}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const response = await fetch(
|
||||
`${CAS}/serviceValidate?service=${service}&ticket=${ticket}`,
|
||||
);
|
||||
const body = parse(await response.text()) as [RegularTagNode];
|
||||
const casResponse = body[0].children[0] as CasResponse;
|
||||
|
||||
if (casResponse.tagName != "cas:authenticationSuccess") {
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers: {
|
||||
Location: `${CAS}/login?service=${service}`,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const headers = new Headers();
|
||||
|
||||
setCookie(headers, {
|
||||
name: "sessionToken",
|
||||
value: await createUserJWT(casResponse),
|
||||
});
|
||||
headers.set("Location", "/apps");
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
headers,
|
||||
});
|
||||
},
|
||||
};
|
||||
|
||||
+11
-7
@@ -1,18 +1,22 @@
|
||||
import { Handlers } from "$fresh/server.ts";
|
||||
import { State } from "$root/routes/_middleware.ts";
|
||||
import { FreshContext, Handlers } from "$fresh/server.ts";
|
||||
import { State } from "$root/defaults/interfaces.ts";
|
||||
import { deleteCookie } from "$std/http/cookie.ts";
|
||||
|
||||
const SERVICE = "https://localhost/";
|
||||
const CAS = "https://ident.univ-amu.fr/cas";
|
||||
|
||||
// deno-lint-ignore no-explicit-any
|
||||
export const handler: Handlers<any, State> = {
|
||||
GET(_request, context) {
|
||||
export const handler: Handlers<null, State> = {
|
||||
/**
|
||||
* Logout of amU CAS SSO system.
|
||||
* @param _request The HTTP incomming request.
|
||||
* @param context The Fresh context with `State`.
|
||||
* @returns A redirect response to either CAS logout or home.
|
||||
*/
|
||||
GET(_request: Request, context: FreshContext<State, null>): Response {
|
||||
if (context.state.isAuthenticated) {
|
||||
const headers = new Headers();
|
||||
|
||||
deleteCookie(headers, "sessionToken", { path: "/" });
|
||||
headers.set("Location", `${CAS}/logout?service=${SERVICE}`);
|
||||
headers.set("Location", `${CAS}/logout?service=${context.url.origin}`);
|
||||
|
||||
return new Response(null, {
|
||||
status: 302,
|
||||
|
||||
Reference in New Issue
Block a user