Personal information
Name: Rohan Sasne
Email address: rohansasne30@gmail.com
University: Vishwakarma Institute of Technology, Pune
Country of Residence: India
Time Zone: IST (GMT + 0530)
Administration system
Project Overview
The BookBrainz Administration System project aims to develop a flexible privilege hierarchy system to allow users to have special privileges according to their roles. The project’s primary goal is to devise and implement a basic admin system for BookBrainz, allowing admins to search for users, give them privileges, and perform other actions. The project will also implement middleware to secure specific routes based on a user’s role. Admins will have access to the admin panel, allowing them to block or delete abusive users, and privileged editors will be able to edit relationships and identifiers and trigger a reindex of the search server. The project has extended goals, including adding a page for privileged users to edit and add relationships and identifiers and creating a public log of administration actions.
My PR’s: Check Out
My Commits: Check Out
Implementation:
- Dropdown to access all pages:
Dropdown to access privileged pages
Here we’ll create a dropdown named as privileges which will have routes for all the other actions.
In the above image, we have considered that the current user is the admin, and accordingly we have given him full privileges, for any other editor with limited privileges, we can check the privileges column in the editors table (which will be added during the project) and restrict access to pages (or we can only show the options in the dropdown which the editor has privilege to) of the editor by checking against the binary bit the user has in his/her privileges column.
- Implement the admin panel webpage:
UI mockup for Admin Page
To implement a simple admin panel webpage, we can create a new route that serves the admin panel HTML page and corresponding client-side JavaScript code to interact with the server.
Here’s an example of how I will implement this:
// Route to serve the admin panel HTML page
app.get("/admin-panel", hasViewAccess, (req, res) => {
// I'll use a template engine (e.g. Handlebars) to render the admin panel HTML page,
// passing in any relevant data (e.g. the list of all users, their roles, etc.)
res.render("admin-panel", { users: getAllUsers() });
});
// Route to update a user's role (assuming we have a 'users' table with a 'role' column)
app.post("/users/:id/update-role", hasUpdatePrivilegesAccess, (req, res) => {
const userId = req.params.id;
const newRole = req.body.role; // Assuming the new role is passed in as a POST parameter
updateUserRole(userId, newRole); // Assuming a function to update a user's role in the database
res.redirect("/admin-panel"); // Redirect back to the admin panel page
});
// Route to delete a user (assuming we have a 'users' table)
app.post("/users/:id/delete", hasDeleteAndBlockAccess, (req, res) => {
const userId = req.params.id;
deleteUser(userId); // A function to delete a user from the database
res.redirect("/admin-panel"); // Redirect back to the admin panel page
});
In the above code, we define a new route ‘/admin-panel’ that is accessible only to admins using the middleware functions (defined in the middleware section below). The route handler renders the admin-panel HTML template using a template engine (e.g. Handlebars) and passes in any relevant data (e.g. the list of all users and their roles).
On the client-side, we can write JavaScript code to interact with the server through AJAX requests to update a user’s role or delete a user. For example:
// Example client-side code to update a user's role using AJAX
const userId = "123"; // Will be replaced with the ID of the user to update
const newRole = "privileged_editor"; // Will be replaced with the new role
fetch(`/users/${userId}/update-role`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ role: newRole }),
})
.then((res) => res.json())
.then((data) => {
console.log(data); // Will Handle success/failure here
})
.catch((err) => console.error(err));
// Example client-side code to delete a user using AJAX
const userId = "123"; // Will be Replaced with the ID of the user to delete
fetch(`/users/${userId}/delete`, {
method: "POST",
})
.then((res) => res.json())
.then((data) => {
console.log(data); // Handle success/failure here
})
.catch((err) => console.error(err));
// Get the list of users as an admin
fetch("/users", {
method: "GET",
headers: {
Authorization: "Bearer " + token, // Replace token with actual JWT token
},
})
.then((response) => {
if (!response.ok) {
throw new Error("Network response was not ok");
}
return response.json();
})
.then((data) => {
console.log(data); // Display the list of users in the console
})
.catch((error) => {
console.error("There was a problem fetching the list of users:", error);
});
The routes handle updating a user’s role and deleting a user. These routes are accessed via AJAX requests from the client-side JavaScript code. We use the fetch API to send a POST request to the server with the relevant information (e.g. the user ID and the new role for updating a user’s role) in the request body. The server then updates the database and sends a response back to the client-side JavaScript code, which handles success or failure accordingly.
By using AJAX requests, we can update or delete users without having to reload the entire webpage, providing a more seamless user experience.
- Middleware for Securing Specific Routes
We’ll be using bit masking to authenticate whether the requested action is allowed for that particular user or not, firstly we define the binary strings as follows:
const EDITOR_ACCESS: 1 = 1;
const VIEW_USER_ACCESS: 2 = 2;
const DELETE_AND_BLOCK_USER_ACCESS: 4 = 4;
const REINDEX_ACCESS: 8 = 8;
const RELATIONSHIP_TYPE_EDIT_ACCESS: 16 = 16;
const IDENTIFIER_TYPE_EDIT_ACCESS: 32 = 32;
const UPDATE_PRIVILEGES_ACCESS: 64 = 64;
If a user was blocked/ deleted, we’ll make the EDITOR_ACCESS bit to 0, thereby giving us the liberty to not create extra tables to define status of current editor, i.e. whether he’s blocked or deleted.
We’ll also be updating the existing editors table of bookbrainz and add a column of privileges in order to store the binary string which represents the level of access each user would have.
We’ll have a users table in our database, and each user has a privilege. To secure the routes based on a user’s role, we can define middleware functions that check if the user making the request has the required privilege. We’ll perform the AND operation and if the answer is non zero, that means TRUE else FALSE. Here is the example snippet:
// Middleware function to check if user is an view Access
function hasViewAccess(req, res, next, editor: EditorPropT) {
if ((editor.privileges & VIEW_USER_ACCESS) > 0) {
// If user has access, call next() to proceed to the next middleware/route handler
next();
} else {
// If user does not have access, redirect to a different route or show an error page
res.redirect('/login');
}
}
// Middleware function to check if user is a privileged editor
function hasRelationshipTypeEditAccess(req, res, next editor: EditorPropT) {
if (editor.privileges & RELATIONSHIP_TYPE_EDIT_ACCESS) > 0) {
// If user is a privileged editor, call next() to proceed to the next middleware/route handler
next();
} else {
// If user is not a privileged editor, redirect to a different route or show an error page
res.redirect('/login');
}
}
// Similarly the pseudo functions for all other access are defined below:
function hasDeleteAndBlockAccess(req, res, next, editor: EditorPropT) {
if ((editor.privileges & DELETE_AND_BLOCK_USER_ACCESS) > 0) {
next();
} else {
res.redirect('/login');
}
}
function hasReindexAccess(req, res, next, editor: EditorPropT) {
if ((editor.privileges & REINDEX_ACCESS) > 0) {
next();
} else {
res.redirect('/login');
}
}
function hasIdentifierTypeEditAccess(req, res, next, editor: EditorPropT) {
if ((editor.privileges & IDENTIFIER_TYPE_EDIT_ACCESS) > 0) {
next();
} else {
res.redirect('/login');
}
}
function hasUpdatePrivilegesAccess(req, res, next, editor: EditorPropT) {
if ((editor.privileges & UPDATE_PRIVILEGES_ACCESS) > 0) {
next();
} else {
res.redirect('/login');
}
}
We can then use these middleware functions to secure the relevant routes:
// Route to view the admin panel, accessible only by admins
app.get('/admin-panel', hasViewAccess, (req, res) => {
// Handle request/response here
});
// Route to view a list of users, accessible only by admins
app.get('/users', hasViewAccess, (req, res) => {
// Handle request/response here
});
// Route to block abusive users, accessible only by admins
app.post('/users/:id/block', hasDeleteAndBlockAccess, (req, res) => {
// Handle request/response here
});
// Route to delete abusive users, accessible only by admins
app.delete('/users/:id', hasDeleteAndBlockAccess, (req, res) => {
// Handle request/response here
});
// Route to edit relationships, accessible only by privileged editors
app.put('/entities/:id/relationships', hasRelationshipTypeEditAccess, (req, res) => {
// Handle request/response here
});
// Route to edit identifiers, accessible only by privileged editors
app.put('/entities/:id/identifiers, hasIdentifierTypeEditAccess, (req, res) => {
// Handle request/response here
});
// Route to trigger a reindex of the search server, accessible only by privileged editors
app.post('/search/reindex', hasReindexAccess, (req, res) => {
// Handle request/response here
});
// Route to update a user's role and privileges, implementation is pseudo and all other endpoints would be implemented using similar logic and as per use case
app.put('/users/:id/role', hasUpdatePrivilegesAccess, async (req, res) => {
try {
const userId = req.params.id;
const { roleId, privileges } = req.body;
// Check if the role ID is valid
const role = await Role.findById(roleId);
if (!role) {
return res.status(400).json({ message: "Invalid role ID" });
}
// Update the user's role
const user = await User.findById(userId);
if (!user) {
return res.status(400).json({ message: "Invalid user ID" });
}
user.role = roleId;
user.privileges = privileges;
await user.save();
res.json({ message: "User role and privileges updated successfully" });
} catch (error) {
console.error(error);
res.status(500).json({ message: "Internal server error" });
}
});
In the above code, we use the middleware functions as the second argument to the route handlers for the admin panel, blocking abusive users, and deleting users, to ensure that only admins can access those routes. Similarly, we use the other middleware function as the second argument to the route handlers for editing relationships/identifiers and triggering a reindex of the search server, to ensure that only privileged editors can access those routes.
- Page that allows privileged users to edit and add relationships and identifiers
An extended goal for the BookBrainz administration system would be to create a page that allows privileged users to edit and add relationships and identifiers.
To implement this, we will create a new page within the admin panel that only privileged users can access. This page could have forms for editing relationships and identifiers, with appropriate validation and error handling to ensure that data is entered correctly.
Here’s an example of how we will structure the routing and middleware for this page using ExpressJS:
// Route to handle form submission for editing relationships
router.post('/edit/relationships', hasRelationshipTypeEditAccess, (req, res) => {
// Handle form submission and update database accordingly
});
// Route to handle form submission for adding identifiers
router.post('/edit/identifiers', hasIdentifierTypeEditAccess, (req, res) => {
// Handle form submission and update database accordingly
});
In this example, we define a middleware function hasIdentifierTypeEditAccess and hasRelationshipTypeEditAccess checks if the user is logged in and has the privileged role.
We then define routes for displaying the form for editing relationships and identifiers, as well as routes for handling form submissions. These routes are protected by the hasRelationshipTypeEditAccess and hasIdentifierTypeEditAccess middlewares to ensure that only privileged users can access them.
In the form submission routes, we would need to handle the data submitted by the user, validate it, and update the database accordingly. This will involve querying the BookBrainz database and using SQL to update or insert new relationships and identifiers.
- A public log of administration actions
Another extended goal for the BookBrainz administration system is to create a public log of administration actions, similar to what is done on CritiqueBrainz.
To implement this, we will create a new database table to store information about administration actions, such as who performed the action, what the action was, and when it occurred. We could then create a new page within the admin panel that displays this log to privileged users, and a public page that displays a subset of this log to regular users.
Page to display administration action logs
Here’s an example of how we will structure the database schema for the administration log:
CREATE TABLE admin_log (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT,
action_type VARCHAR(255),
action_details TEXT,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP
);
In this schema, we have a table called admin_log that stores information about administration actions. Each row in the table represents a single action, and includes fields for the user who performed the action (user_id), the type of action performed (action_type), details about the action (action_details), and the timestamp when the action occurred (timestamp).
Here’s an example of how we could structure the routing and middleware for the public log page using ExpressJS:
// Route to display the public log to regular users
router.get('/log', (req, res) => {
// Query the database for the 10 most recent admin actions and render them to the page
res.render('public_log', { logEntries: logEntries });
});
// Route to display the full log to privileged users
router.get('/admin/log', hasUpdatePrivilegesAccess, (req, res) => {
// Query the database for all admin actions and render them to the page
res.render('admin_log', { logEntries: logEntries });
});
In this example, we define a middleware function hasUpdatePrivilegesAccess that checks if the user is logged in and has the privileged role. If they do, the middleware calls next() to continue processing the request. If not, the middleware redirects the user to the admin panel homepage.
We then define two routes: one for displaying the public log to regular users, and one for displaying the full log to privileged users. In each route, we query the database for the relevant log entries and render them to the appropriate view.
Timeline:
Community Bonding Period ( May 4 - May 28)
- Discuss the project details with the mentor and clarify any doubts
- Read the documentation again to enhance knowledge about the project.
- Get up to speed to begin working on their projects.
- Set up the development environment and install necessary dependencies (If any were missing previously)
Week 1 (May 29 - June 4):
- Implement the basic admin panel web page with functionality to search for users and view their details
- Create a dropdown to access certain privileged routes
Week 2 (June 5 - June 11):
- Test the implemented features and fix any issues found during testing
- Begin working on the middleware for securing specific routes
Week 3 (June 12 - June 18):
- Complete the middleware implementation for securing specific routes based on user roles
- Add functionality to allow admins to give users privileges and block or delete abusive users
Week 4 (June 19 - June 25):
- Implement the ability for privileged editors to trigger a reindex of the search server
- Test the implemented feature and fix any issues found during testing
Week 5 (June 26 - July 2):
- Work on improving the user interface of the admin panel
- Refactor the code and make any necessary improvements
Week 6 (July 3 - July 9):
- Start documenting the implemented features and writing user guides
- Prepare for the mid-term evaluation and keep all the required documents ready.
Week 7 (July 10 - July 16):
- Test the overall functionality of the admin system and ensure it meets the requirements
- Discuss the progress and next steps with the mentor
- Add Middleware to allow privileged editors to edit relationships and identifier
Week 8 (July 17 - July 23):
- A web interface to allow privileged users to edit and add relationship types , identifier types
Week 9 (July 24 - July 30):
- Begin working on the extended goal of a public log of administration actions
- Implement the feature to log administration actions and display them publicly
Week 10 (July 31 - August 6):
- Test the implemented feature and fix any issues found during testing
- Add any final touches to the admin system before submission
Week 11 (August 7 - August 13):
- Document the implemented extended features and writing user guides
- Address any issues that may arise during the final evaluation
- Make any necessary improvements based on feedback from the evaluation
Week 12 (August 14 - August 21):
- Prepare to submit the code and all required documentation
- Celebrate the completion of the project and reflect on the experience.
Detailed information about yourself
Hello, I’m Rohan Sasne, currently in my second year pursuing my bachelors degree in Computer Engineering from Vishwakarma Institute of Technology (VIT), Pune, India. I’m Deeply passionate about open source projects and open source communities .
Tell us about the computer(s) you have available for working on your SoC project!
I own Asus Tuf F15 gaming laptop with 8 GB of RAM and 512 GB of secondary storage.
When did you first start programming?
I started programming right after my 12th standard board exams.
What type of music do you listen to? (Please list a series of MBIDs as examples.)
I generally listen to rap music like one’s from Eminem and rock music from Linken Park.
If applying for a BookBrainz project: what type of books do you read? (Please list a series of BBIDs as examples. (And feel free to also list music you listen to!))
I love reading epic fantasies and i am big fan of Harry Potter Series and romantic novels like The Fault in Our Stars
What aspects of the project you’re applying for (e.g., MusicBrainz, AcousticBrainz, etc.) interest you the most?
BookBrainz offers a free, public database that makes it possible for ardent book readers like me to access relevant book information and fully utilise collections.
Have you contributed to other Open Source projects? If so, which projects and can we see some of your code?
Yes I have contributed to some open source projects and my most recent contribution was to a project named Deca-Org which can be found here where we have built a organisation management portal to facilitate smooth functioning within organisations. You can have a look at my Github profile to get a clear view of my profile.
What sorts of programming projects have you done on your own time?
I have done projects ranging from the domains of Web Development,Machine Learning and my most recently I have been doing projects in the field of DevOps. I have developed projects like Decentralised Social Media Application, Unique Dog Identification using CNN, Advanced Driver Safety System using CNN, etc.
How much time do you have available, and how would you plan to use it?
I plan to devote 20 hours per week starting May 28. My university exams will end on May 26.