Skip to content

Commit fe144ee

Browse files
committed
admin groups
1 parent ac4b4c4 commit fe144ee

File tree

4 files changed

+124
-3
lines changed

4 files changed

+124
-3
lines changed

config.js

+3-1
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
require('dotenv').config()
22

33

4-
exports.groups_permitted = process.env.GROUPS_PERMITTED ? process.env.GROUPS_PERMITTED.split(',') : ['ACM Link Shortener Managers', 'ACM Exec', 'ACM Infra Leadership'];
4+
exports.groups_permitted = process.env.GROUPS_PERMITTED ? process.env.GROUPS_PERMITTED.split(',') : ['ACM Link Shortener Managers', 'ACM Exec', 'ACM Officers', 'ACM Infra Leadership'];
5+
6+
exports.admin_groups = process.env.ADMIN_GROUPS ? process.env.ADMIN_GROUPS.split(',') : ['ACM Infra Leadership'];
57

68
exports.branding = {
79
title: process.env.brandTitle || "ACM Link Shortener",

index.js

+54-1
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,7 @@ async function addURLToDB(name, url, email, groups) {
166166
})
167167
})
168168
}
169+
169170
async function getDataForEmail(email) {
170171
return new Promise(function(resolve, reject) {
171172
db.serialize(function() {
@@ -181,6 +182,22 @@ async function getDataForEmail(email) {
181182
})
182183
}
183184

185+
async function getAllLinks() {
186+
return new Promise(function(resolve, reject) {
187+
db.serialize(function() {
188+
const stmt = db.prepare("SELECT * FROM urlData");
189+
stmt.all([], function(err, data) {
190+
if (err) {
191+
reject(err)
192+
} else {
193+
resolve(data)
194+
}
195+
})
196+
})
197+
})
198+
}
199+
200+
184201
async function getDelegatedLinks(userGroups) {
185202
return new Promise(function(resolve, reject) {
186203
db.serialize(function() {
@@ -332,12 +349,22 @@ app.use(async (req, res, next) => {
332349
if (!req.user) {return next();}
333350
req.user._json.groups = await getUserGroups(req.user.oid, gat);
334351
const intserect = validateArray(config.groups_permitted, req.user._json.groups);
335-
if (!intserect){
352+
const intersect2 = validateArray(config.admin_groups, req.user._json.groups)
353+
if (!intserect && !intersect2){
336354
return res.status(401).redirect("/unauthorized");
337355
}
338356
next();
339357
})
340358

359+
app.use('/admin/', async (req, res, next) => {
360+
if (!req.user) {return next();}
361+
req.user._json.groups = await getUserGroups(req.user.oid, gat);
362+
const intersect2 = validateArray(config.admin_groups, req.user._json.groups)
363+
if (!intersect2){
364+
return res.status(401).redirect("/unauthorized");
365+
}
366+
next();
367+
})
341368
// begin business logic
342369

343370
app.get('/', async function (req, res) {
@@ -426,6 +453,32 @@ app.get('/mylinks', ensureAuthenticated, async function (req, res) {
426453
})
427454
})
428455

456+
app.get('/admin/links', ensureAuthenticated, async function (req, res) {
457+
const email = req.user._json.preferred_username;
458+
const name = req.user.displayName;
459+
const userGroups = req.user._json.groups !== undefined ? req.user._json.groups : [];
460+
let data = await getAllLinks().catch(() => {res.status(500).render('500', {productName: config.branding.title, logoPath: config.branding.logoPath, copyrightOwner: config.branding.copyrightOwner, statusURL: config.branding.statusURL,}); return});
461+
data = data.map((item) => {
462+
const d = item;
463+
d.url = atob(d.url);
464+
d.groups = d.groups.replace(',', "<br />")
465+
return d;
466+
})
467+
res.render('adminlinks', {
468+
partials,
469+
productName: config.branding.title,
470+
logoPath: config.branding.logoPath,
471+
copyrightOwner: config.branding.copyrightOwner,
472+
statusURL: config.branding.statusURL,
473+
orgHome: config.branding.orgHome,
474+
data,
475+
name,
476+
email,
477+
baseURL,
478+
productName: config.branding.title
479+
})
480+
})
481+
429482
app.delete('/deleteLink', ensureAuthenticated, async function (req, res) {
430483
const name = req.query.name;
431484
removeURLfromDB(name).then(() => {

package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "linkry-link-shortener",
3-
"version": "1.4.1",
3+
"version": "1.5.0",
44
"description": "",
55
"main": "index.js",
66
"scripts": {

view/adminlinks.html

+66
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,66 @@
1+
<!DOCTYPE html>
2+
<html lang="en">
3+
<noscript>Your browser does not support JavaScript, but this site requires JavaScript!</noscript>
4+
5+
<head>
6+
<title>{{{productName}}}</title>
7+
<meta property="og:title" content="{{{productName}}}" />
8+
<link href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css" rel="stylesheet"
9+
integrity="sha384-giJF6kkoqNQ00vy+HMDP7azOuL0xtbfIcaT9wjKHr8RbDVddVHyTfAAsrekwKmP1" crossorigin="anonymous">
10+
<link rel="stylesheet" href="/static/css/epoch.css">
11+
<script src="/static/js/mylinks.js"></script>
12+
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js" integrity="sha384-MrcW6ZMFYlzcLA8Nl+NtUVF0sA7MsXsP1UyJoMp4YLEuNSfAP+JcXn/tWtIaxVXM" crossorigin="anonymous"></script>
13+
</head>
14+
<script>
15+
async function handleRemove(name) {
16+
const sure = confirm(`Are you sure you would like to remove this link? Anyone visiting {{baseURL}}/${name} will recieve an error!`);
17+
if (!sure) {
18+
alert("Remove cancelled.")
19+
return
20+
}
21+
const response = await fetch(`/deleteLink?name=${name}`, {
22+
method: "DELETE"
23+
})
24+
const parsed = await response.json()
25+
if (response.status !== 200) {
26+
alert(parsed.message)
27+
} else {
28+
alert("URL removed successfully!")
29+
document.getElementById(`urldata-${name}`).parentNode.removeChild(document.getElementById(`urldata-${name}`))
30+
}
31+
}
32+
</script>
33+
34+
<body class="epoch-dark epoch-font epoch-child-light">
35+
{{>fullNavbar}}
36+
<br />
37+
<div class="container">
38+
<h1 style="text-align: center;">All shortened links</h1>
39+
<br />
40+
<table style="width:100%" class="table table-striped">
41+
<tr>
42+
<th style="text-align: center;" scope="col">Original</th>
43+
<th style="text-align: center;" scope="col">Shortened</th>
44+
<th style="text-align: center;" scope="col">Owner</th>
45+
<th style="text-align: center;" scope="col">Groups</th>
46+
<th style="text-align: center;" scope="col">Actions</th>
47+
</tr>
48+
{{#data}}
49+
<tr id="urldata-{{name}}">
50+
<td style="text-align: center;"><a id="urldata-{{name}}-url" href="{{url}}">{{url}}</a></td>
51+
<td style="text-align: center;"><a href="/{{name}}">{{baseURL}}/{{name}}</a></td>
52+
<td style="text-align: center;"><a>{{email}}</a></td>
53+
<td style="text-align: center;"><a>{{{groups}}}</a></td>
54+
<td style="text-align: center;"><button type="button" class="btn btn-danger"
55+
onclick="handleRemove('{{name}}', '{{url}}')">Remove Link</button><a>&nbsp;</a><button
56+
type="button" class="btn btn-warning" onclick="handleEdit('{{name}}')">Edit Link</button></a>
57+
</td>
58+
</tr>
59+
{{/data}}
60+
</table>
61+
</div>
62+
{{> footer}}
63+
64+
</body>
65+
66+
</html>

0 commit comments

Comments
 (0)