diff --git a/package-lock.json b/package-lock.json
index 48a3b1d..a1305d1 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -9,7 +9,6 @@
"version": "0.1.0",
"dependencies": {
"@azure/identity": "^4.0.1",
- "@azure/msal-browser": "^3.10.0",
"@azure/msal-node": "^2.7.0",
"@hookform/resolvers": "^3.3.4",
"@microsoft/microsoft-graph-client": "^3.0.7",
diff --git a/package.json b/package.json
index 2c47ce3..f19a6aa 100644
--- a/package.json
+++ b/package.json
@@ -14,11 +14,11 @@
"prisma:push": "dotenv -e .env.development -- npx prisma db push",
"prisma:migrate": "dotenv -e .env.development -- npx prisma migrate dev",
"prisma:generate": "dotenv -e .env.development -- npx prisma generate",
+ "seed:admin": "dotenv -e .env.development -e .env.local -- ts-node --project tsconfig.seed.json prisma/seed-admin.ts",
"seed:dev": "dotenv -e .env.development -- ts-node --project tsconfig.seed.json prisma/seed-dev.ts"
},
"dependencies": {
"@azure/identity": "^4.0.1",
- "@azure/msal-browser": "^3.10.0",
"@azure/msal-node": "^2.7.0",
"@hookform/resolvers": "^3.3.4",
"@microsoft/microsoft-graph-client": "^3.0.7",
diff --git a/prisma/schema.prisma b/prisma/schema.prisma
index da9c5a6..524fae3 100644
--- a/prisma/schema.prisma
+++ b/prisma/schema.prisma
@@ -11,8 +11,7 @@ datasource db {
}
enum Role {
- appAdmin @map("app_admin")
- taxesAdmin @map("taxes_admin")
+ appAdmin @map("app_admin")
}
model User {
diff --git a/prisma/seed-admin.ts b/prisma/seed-admin.ts
new file mode 100644
index 0000000..0557a18
--- /dev/null
+++ b/prisma/seed-admin.ts
@@ -0,0 +1,111 @@
+import msal from "@azure/msal-node";
+import {
+ AuthProvider,
+ AuthProviderCallback,
+ Client,
+} from "@microsoft/microsoft-graph-client";
+import { PrismaClient, Role } from "@prisma/client";
+import { createInterface as readlineCreateInterface } from "readline";
+import { stdin as input, stdout as output } from "process";
+
+const prisma = new PrismaClient();
+
+const MICROSOFT_GRAPH_ENDPOINT = "https://graph.microsoft.com/";
+
+async function acquireAccessToken() {
+ const tenantId = process.env.AZURE_AD_TENANT_ID;
+ const clientId = process.env.AZURE_AD_CLIENT_ID;
+ const clientSecret = process.env.AZURE_AD_CLIENT_SECRET;
+
+ if (!tenantId) {
+ throw Error("Entra ID tenant ID is missing");
+ }
+
+ if (!clientId) {
+ throw Error("Entra ID client ID is missing");
+ }
+
+ if (!clientSecret) {
+ throw Error("Entra ID client secret is missing");
+ }
+
+ const msalConfig = {
+ auth: {
+ authority: `https://login.microsoftonline.com/${tenantId}`,
+ clientId,
+ clientSecret,
+ },
+ };
+
+ const tokenRequest = {
+ scopes: [`${MICROSOFT_GRAPH_ENDPOINT}/.default`],
+ };
+
+ const cca = new msal.ConfidentialClientApplication(msalConfig);
+
+ return await cca.acquireTokenByClientCredential(tokenRequest);
+}
+
+async function main() {
+ const authenticationResult = await acquireAccessToken();
+ if (!authenticationResult) {
+ throw Error("Failed to acquire access token for MSAL API");
+ }
+
+ const { accessToken } = authenticationResult;
+
+ const rl = readlineCreateInterface({ input, output });
+
+ rl.question(
+ "New admin user institutional e-mail address: ",
+ async (email: string) => {
+ const authProvider: AuthProvider = async (
+ callback: AuthProviderCallback,
+ ) => callback(null, accessToken);
+
+ const client = Client.init({ authProvider });
+
+ let userInfo;
+ try {
+ userInfo = await client
+ .api(`/users/${email}?$select=id,displayName`)
+ .get();
+ } catch (exception) {
+ throw new Error(
+ `Failed to get user data from Microsoft 365: ${exception}`,
+ );
+ }
+
+ if (!userInfo || !userInfo["id"]) {
+ throw new Error("User with given e-mail address could not be found");
+ }
+
+ try {
+ const newUser = await prisma.user.create({
+ data: {
+ azureAdObjectId: userInfo["id"],
+ role: Role.appAdmin,
+ },
+ });
+
+ console.log(
+ `Successfully created admin account associated to user with ID ${newUser.azureAdObjectId}, full name ${userInfo["displayName"]}`,
+ );
+ } catch (exception) {
+ console.error(`Failed to create user in database: ${exception}`);
+ }
+
+ rl.close();
+ },
+ );
+}
+
+main()
+ .then(async () => {
+ await prisma.$disconnect();
+ })
+ .catch(async (e) => {
+ console.error(e);
+ await prisma.$disconnect();
+ process.exit(1);
+ });
diff --git a/prisma/seed-dev.ts b/prisma/seed-dev.ts
index d957e56..6a597e6 100644
--- a/prisma/seed-dev.ts
+++ b/prisma/seed-dev.ts
@@ -40,18 +40,6 @@ async function main() {
studentDorms.push(studentDorm);
}
- // Insert 5 User records
- const users = [];
- for (let i = 1; i <= 25; i++) {
- const user = await prisma.user.create({
- data: {
- azureAdObjectId: `UserObjectID${i}`,
- role: i % 2 === 0 ? Role.appAdmin : Role.taxesAdmin, // Alternate between roles
- },
- });
- users.push(user);
- }
-
const facultyTaxValues = [];
for (let i = 1; i <= 25; i++) {
let studyCycle: StudyCycle = StudyCycle.bachelors;
diff --git a/src/actions/actions.tsx b/src/actions/actions.tsx
index 99f6379..410d0fd 100644
--- a/src/actions/actions.tsx
+++ b/src/actions/actions.tsx
@@ -295,17 +295,6 @@ export async function addAdmin(formData: FormData) {
redirect("/admin/users");
}
-export async function addTaxesAdmin(formData: FormData) {
- await prisma.user.create({
- data: {
- azureAdObjectId: formData.get("userId") as string,
- role: Role.taxesAdmin,
- },
- });
- revalidatePath("/");
- redirect("/admin/users");
-}
-
export async function deleteAdmin(formData: FormData) {
const id = formData.get("userId") as string;
await prisma.user.delete({
diff --git a/src/app/admin/auth/signin/SignInButton.tsx b/src/app/admin/auth/sign-in/SignInButton.tsx
similarity index 100%
rename from src/app/admin/auth/signin/SignInButton.tsx
rename to src/app/admin/auth/sign-in/SignInButton.tsx
diff --git a/src/app/admin/auth/signin/page.tsx b/src/app/admin/auth/sign-in/page.tsx
similarity index 100%
rename from src/app/admin/auth/signin/page.tsx
rename to src/app/admin/auth/sign-in/page.tsx
diff --git a/src/components/navbar.tsx b/src/components/navbar.tsx
index 56c4637..7fd469a 100644
--- a/src/components/navbar.tsx
+++ b/src/components/navbar.tsx
@@ -1,7 +1,9 @@
"use client";
import React from "react";
+
import Link from "next/link";
+import { signOut } from "next-auth/react";
function Navbar() {
return (
@@ -9,6 +11,9 @@ function Navbar() {
Home
+
);
}
diff --git a/src/next-auth-options.ts b/src/next-auth-options.ts
index 1b67fd7..133570b 100644
--- a/src/next-auth-options.ts
+++ b/src/next-auth-options.ts
@@ -87,7 +87,7 @@ export const authOptions: NextAuthOptions = {
},
},
pages: {
- signIn: "/admin/auth/signin",
+ signIn: "/admin/auth/sign-in",
},
};