From b194d2031da8f19c9b39e7f2697b175cc6c3e68d Mon Sep 17 00:00:00 2001 From: Siwat Sirichai Date: Sun, 5 May 2024 18:13:58 +0700 Subject: [PATCH] working ish website --- config/passport.js | 20 ++++++- directory.js | 42 +++++++++++++- index.js | 11 ++++ routes/auth.js | 12 +++- routes/ps_relation_parent.js | 20 ++++++- routes/ps_relation_student.js | 7 +++ statics/selfservice/index.html | 71 ++++++++++++++++++++++++ statics/selfservice/student.html | 94 ++++++++++++++++++++++++++++++++ 8 files changed, 271 insertions(+), 6 deletions(-) create mode 100644 statics/selfservice/index.html create mode 100644 statics/selfservice/student.html diff --git a/config/passport.js b/config/passport.js index f1e16e7..42cc58c 100644 --- a/config/passport.js +++ b/config/passport.js @@ -33,7 +33,25 @@ passport.use( console.log("user:", user); profile["dn"] = user.dn; profile["memberOf"] = user.memberOf; - return done(null, profile); + // Is this user a student or a parent? + profile["userType"] = directory.getUserTypeFromDN(profile["dn"]); + let user_type = profile["userType"]; + // If the user is a student, query the student's primary parent + // and store the parent's UPN in the session + if (user_type === directory.USER_TYPE.STUDENT) { + let student = await directory.queryUser(username, ["primaryParent"]); + profile["primaryParent"] = student.primaryParent; + } + // If the user is a parent, query the parent's students + // and store the students' UPNs in the session + else if (user_type === directory.USER_TYPE.PARENT) { + let students = await directory.listStudents(username); + profile["students"] = students; + } else { + console.log("Unknown user type"); + } + return done(null, profile); // Return the user's profile + } ) ); diff --git a/directory.js b/directory.js index fe226e5..3e886e3 100644 --- a/directory.js +++ b/directory.js @@ -67,10 +67,19 @@ const USER_TYPE = { // Determine the type of user // Student is in OU=Students,OU=Users,DC=ad,DC=satitm,DC=chula,DC=ac,DC=th // Parent is in OU=Parents,OU=Users,DC=ad,DC=satitm,DC=chula,DC=ac,DC=th -function getUserType(req, res) { +function getUserType(req) { // The user's DN is present in the session as req.user.dn + if (req.user) { + return getUserTypeFromDN(req.user.dn); + } + else { + return USER_TYPE.UNKNOWN; + } +} + +function getUserTypeFromDN(dn) { // To convert DN to OU, remove from first CN= to first , - let ou = req.user.dn.substring(req.user.dn.indexOf(',') + 1); + let ou = dn.substring(dn.indexOf(',') + 1); console.log('OU:', ou); if (ou === 'OU=Students,DC=ad,DC=satitm,DC=chula,DC=ac,DC=th') { return USER_TYPE.STUDENT; @@ -96,9 +105,38 @@ async function getPrimaryParent(student_upn) { } } +async function listStudents(upn) { + // Search for students with the parent's UPN in their primaryParent attribute + let opts = { + filter: `(primaryParent=${upn})`, + scope: 'sub', + attributes: ['userPrincipalName'] + }; + let students = []; + return new Promise((resolve, reject) => { + satitm_directory.search('DC=ad,DC=satitm,DC=chula,DC=ac,DC=th', opts, function(err, ldapRes) { + ldapRes.on('searchEntry', function(entry) { + console.log('entry: ' + JSON.stringify(entry.object)); + students.push(entry.object.userPrincipalName); + }); + ldapRes.on('error', function(err) { + console.error('error: ' + err.message); + reject(err); + }); + ldapRes.on('end', function(result) { + console.log('status: ' + result.status); + resolve(students); + }); + }); + }); +} + module.exports = { queryUser: queryUser, getUserType: getUserType, + getUserTypeFromDN: getUserTypeFromDN, setPrimaryParent: setPrimaryParent, + listStudents: listStudents, + getPrimaryParent: getPrimaryParent, USER_TYPE: USER_TYPE }; \ No newline at end of file diff --git a/index.js b/index.js index 42d16c9..9e82223 100644 --- a/index.js +++ b/index.js @@ -20,6 +20,14 @@ app.use(passport.session()); app.use(express.json()); app.use(express.urlencoded({ extended: true })); +app.get('/selfservice/api/whoami', function (req, res) { + if (!req.isAuthenticated()) { + return res.status(401).send('Unauthorized'); + } + // Send user type and upn in json format + res.send({ userType: req.user.userType, upn: req.user.username }); +}); + let authRoutes = require('./routes/auth.js'); app.use('/', authRoutes); let psRelationStudentRoutes = require('./routes/ps_relation_student.js'); @@ -27,6 +35,9 @@ app.use('/selfservice/api', psRelationStudentRoutes); let psRelationParentRoutes = require('./routes/ps_relation_parent.js'); app.use('/selfservice/api', psRelationParentRoutes); +// Serve Static Files +app.use("/",express.static('statics')); + let server = https.createServer(http_config.options, app); server.listen(3000, function () { console.log('Listening on port 3000'); diff --git a/routes/auth.js b/routes/auth.js index 9af0756..24ba195 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -14,6 +14,14 @@ router.get('/selfservice/api', function (req, res) { response += 'Username: ' + req.user.username + '
'; response += 'First Name: ' + req.user.first_name + '
'; response += 'Last Name: ' + req.user.last_name + '
'; + usertype_map = ['Unknown', 'Student', 'Parent']; + response += 'User Type: ' + usertype_map[req.user.userType] + '
'; + if (req.user.userType === directory.USER_TYPE.STUDENT) { + response += 'Primary Parent: ' + req.user.primaryParent + '
'; + } + else if (req.user.userType === directory.USER_TYPE.PARENT) { + response += 'Students: ' + req.user.students + '
'; + } response += 'Logout'; res.send(response); } @@ -25,7 +33,7 @@ router.get('/selfservice/api', function (req, res) { router.get('/selfservice/api/logout', function (req, res) { req.logout(); - res.redirect('/selfservice/api'); + res.redirect('/selfservice'); }); router.get('/selfservice/api/login', @@ -42,7 +50,7 @@ router.get('/selfservice/api', function (req, res) { }); router.post('/selfservice/api/login/postResponse', - passport.authenticate('saml', { failureRedirect: '/selfservice/api',successRedirect: '/selfservice/api', failureFlash: true }), + passport.authenticate('saml', { failureRedirect: '/selfservice',successRedirect: '/selfservice', failureFlash: true }), function (req, res) { console.log('SAML authentication successful'); res.redirect('/selfservice'); diff --git a/routes/ps_relation_parent.js b/routes/ps_relation_parent.js index 940a3e0..c2d377f 100644 --- a/routes/ps_relation_parent.js +++ b/routes/ps_relation_parent.js @@ -39,6 +39,18 @@ router.get('/parent/:parent_upn/add-student', async function (req, res) { if(!req.isAuthenticated()) { return res.status(401).send('Unauthorized'); } + if (!req.query.pairing_code) { + return res.status(400).send('Pairing code not provided'); + } + if (!req.params.parent_upn) { + return res.status(400).send('Parent UPN not provided'); + } + if (req.user.username !== req.params.parent_upn) { + return res.status(403).send('Forbidden, UPN mismatch'); + } + if (req.user.userType !== directory.USER_TYPE.PARENT) { + return res.status(403).send('Forbidden, not a parent'); + } let parent_upn = req.params.parent_upn; // Is the logged in user a parent with the same UPN as the one in the URL? // If not, return a 403 Forbidden response @@ -63,10 +75,16 @@ router.get('/parent/:parent_upn/add-student', async function (req, res) { res.send('Student added'); }); -router.get('/parent/:parent_upn', function (req, res) { +router.get('/parent/:parent_upn', async function (req, res) { if(!req.isAuthenticated()) { return res.status(401).send('Unauthorized'); } + if (req.user.username !== req.params.parent_upn) { + return res.status(403).send('Forbidden, UPN mismatch'); + } + if (req.user.userType !== directory.USER_TYPE.PARENT) { + return res.status(403).send('Forbidden, not a parent'); + } let parent_upn = req.params.parent_upn; // Is the logged in user a parent with the same UPN as the one in the URL? // If not, return a 403 Forbidden response diff --git a/routes/ps_relation_student.js b/routes/ps_relation_student.js index b83704e..22ed922 100644 --- a/routes/ps_relation_student.js +++ b/routes/ps_relation_student.js @@ -3,6 +3,7 @@ let express = require('express'); let router = express.Router(); let passport = require('passport'); +let directory = require('../directory.js'); let database = require('../config/database.js'); let uuid = require('uuid'); @@ -24,6 +25,9 @@ router.get('/student/:upn/pairing-code', function (req, res) { if(!req.isAuthenticated()) { return res.status(401).send('Unauthorized'); } + if (req.user.userType !== directory.USER_TYPE.STUDENT) { + return res.status(403).send('Forbidden, not a student'); + } let upn = req.params.upn; // Is the logged in user a student with the same UPN as the one in the URL? // If not, return a 403 Forbidden response @@ -45,6 +49,9 @@ router.get('/student/:upn', function (req, res) { if(!req.isAuthenticated()) { return res.status(401).send('Unauthorized'); } + if (req.user.userType !== directory.USER_TYPE.STUDENT) { + return res.status(403).send('Forbidden, not a student'); + } let upn = req.params.upn; // Is the logged in user a student with the same UPN as the one in the URL? // If not, return a 403 Forbidden response diff --git a/statics/selfservice/index.html b/statics/selfservice/index.html new file mode 100644 index 0000000..644a792 --- /dev/null +++ b/statics/selfservice/index.html @@ -0,0 +1,71 @@ + + + + Authentication Check + + + + +
+

SATITM
Parent-Student Relationship
Management

+

Please log in to continue

+ + + +
+ + + + \ No newline at end of file diff --git a/statics/selfservice/student.html b/statics/selfservice/student.html new file mode 100644 index 0000000..263662a --- /dev/null +++ b/statics/selfservice/student.html @@ -0,0 +1,94 @@ + + + + +
+
+

Student Information

+

Name:

+

Surname:

+

Email:

+
+ +
+

Linked Parent

+

+
+ + + + + + +
+ + \ No newline at end of file