Compare commits
5 Commits
603e01f2cf
...
master
Author | SHA1 | Date | |
---|---|---|---|
6bd5f8798d | |||
cf94bde3a3 | |||
cb622ebea0 | |||
15585bac29 | |||
59f3a45347 |
12
.gitignore
vendored
12
.gitignore
vendored
@ -1,8 +1,6 @@
|
|||||||
# ---> Composer
|
# Modules installed via NPM
|
||||||
composer.phar
|
node_modules/
|
||||||
/vendor/
|
|
||||||
|
|
||||||
# Commit your application's lock file https://getcomposer.org/doc/01-basic-usage.md#commit-your-composer-lock-file-to-version-control
|
|
||||||
# You may choose to ignore a library lock file http://getcomposer.org/doc/02-libraries.md#lock-file
|
|
||||||
# composer.lock
|
|
||||||
|
|
||||||
|
# Stylesheets and Javascript files generated through Grunt.js
|
||||||
|
public/styles/
|
||||||
|
public/js/
|
||||||
|
65
Gruntfile.js
Normal file
65
Gruntfile.js
Normal file
@ -0,0 +1,65 @@
|
|||||||
|
module.exports = function(grunt) {
|
||||||
|
|
||||||
|
var pkg = grunt.file.readJSON('package.json')
|
||||||
|
|
||||||
|
// Project configuration.
|
||||||
|
grunt.initConfig({
|
||||||
|
'dart-sass': {
|
||||||
|
dist: {
|
||||||
|
options: {
|
||||||
|
style: 'compressed'
|
||||||
|
},
|
||||||
|
files: [{
|
||||||
|
expand: true,
|
||||||
|
cwd: 'assets/sass',
|
||||||
|
src: ['*.sass'],
|
||||||
|
dest: 'public/styles',
|
||||||
|
ext: '.css'
|
||||||
|
}]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
coffee: {
|
||||||
|
options: {
|
||||||
|
sourceMap: true,
|
||||||
|
style: 'compressed'
|
||||||
|
},
|
||||||
|
files: {
|
||||||
|
expand: true,
|
||||||
|
flatten: true,
|
||||||
|
cwd: 'assets/coffee',
|
||||||
|
src: ['*.coffee'],
|
||||||
|
dest: 'public/js',
|
||||||
|
ext: '.js'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
watch: {
|
||||||
|
css: {
|
||||||
|
files: ['assets/sass/*.sass'],
|
||||||
|
tasks: ['dart-sass'],
|
||||||
|
options: {
|
||||||
|
atBegin: true,
|
||||||
|
spawn: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
js: {
|
||||||
|
files: ['assets/coffee/*.coffee'],
|
||||||
|
tasks: ['coffee'],
|
||||||
|
options: {
|
||||||
|
atBegin: true,
|
||||||
|
spawn: false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Load task plugins
|
||||||
|
grunt.loadNpmTasks('grunt-dart-sass');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-coffee');
|
||||||
|
grunt.loadNpmTasks('grunt-contrib-watch');
|
||||||
|
|
||||||
|
// Default task(s).
|
||||||
|
grunt.registerTask('default', ['dart-sass', 'coffee']);
|
||||||
|
|
||||||
|
};
|
2
LICENSE
2
LICENSE
@ -1,4 +1,4 @@
|
|||||||
Copyright (c) <year> <owner>
|
Copyright (c) 2022 Metaunix
|
||||||
|
|
||||||
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
|
||||||
|
|
||||||
|
44
archon.js
Normal file
44
archon.js
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const express = require('express');
|
||||||
|
const session = require('express-session');
|
||||||
|
|
||||||
|
const app = express();
|
||||||
|
const port = 3000;
|
||||||
|
|
||||||
|
// Enable POST data handling
|
||||||
|
app.use(express.json());
|
||||||
|
app.use(express.urlencoded({ extended: true }));
|
||||||
|
|
||||||
|
// Start creating our session config
|
||||||
|
var sess = {
|
||||||
|
secret: 'dark archon', // will need to change this later
|
||||||
|
cookie: {}
|
||||||
|
}
|
||||||
|
if (app.get('env') === 'production') {
|
||||||
|
app.set('trust proxy', 1) // trust first proxy
|
||||||
|
sess.cookie.secure = true // serve secure cookies
|
||||||
|
}
|
||||||
|
// Enable the Express.js session handling
|
||||||
|
app.use(session(sess))
|
||||||
|
|
||||||
|
// Initialize the Twig template engine - this might get swapped for Twing later.
|
||||||
|
app.set('view engine', 'twig');
|
||||||
|
|
||||||
|
// Service static files from public/
|
||||||
|
app.use(express.static('public'));
|
||||||
|
|
||||||
|
// Load middleware
|
||||||
|
authMiddleware = require('./src/middleware/authMiddleware');
|
||||||
|
app.use('/', authMiddleware.authProtected);
|
||||||
|
|
||||||
|
// Load in route handlers
|
||||||
|
indexRoutes = require('./routes/index');
|
||||||
|
authRoutes = require('./routes/auth');
|
||||||
|
|
||||||
|
// Assign routes to handlers
|
||||||
|
app.get('/', indexRoutes.home);
|
||||||
|
app.get('/auth/login', authRoutes.getLogin);
|
||||||
|
app.post('/auth/login', authRoutes.postLogin);
|
||||||
|
|
||||||
|
app.listen(port, () => {
|
||||||
|
console.log(`Example app listening on port ${port}`)
|
||||||
|
});
|
2
assets/coffee/archon.coffee
Normal file
2
assets/coffee/archon.coffee
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
window.onload = () ->
|
||||||
|
console.log('Test.')
|
53
assets/sass/archon.sass
Normal file
53
assets/sass/archon.sass
Normal file
@ -0,0 +1,53 @@
|
|||||||
|
body
|
||||||
|
padding-bottom: 115px
|
||||||
|
background: white
|
||||||
|
|
||||||
|
a
|
||||||
|
color: cornflowerblue
|
||||||
|
transition: all 200ms ease-in-out
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
color: darken(cornflowerblue, 10%)
|
||||||
|
|
||||||
|
input
|
||||||
|
transition: all 200ms ease-in-out
|
||||||
|
|
||||||
|
input[type=submit],
|
||||||
|
button
|
||||||
|
background-color: cornflowerblue
|
||||||
|
color: #f0f0f0
|
||||||
|
transition: all 200ms ease-in-out
|
||||||
|
|
||||||
|
&:hover
|
||||||
|
background-color: darken(cornflowerblue, 10%)
|
||||||
|
color: white
|
||||||
|
|
||||||
|
.u-text-center
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
.container
|
||||||
|
max-width: 1024px
|
||||||
|
|
||||||
|
.container.fluid
|
||||||
|
max-width: 100%
|
||||||
|
|
||||||
|
#header h1
|
||||||
|
text-align: center
|
||||||
|
|
||||||
|
#footer
|
||||||
|
position: fixed
|
||||||
|
left: 0
|
||||||
|
right: 0
|
||||||
|
bottom: 0
|
||||||
|
padding-top: 25px
|
||||||
|
padding-bottom: 25px
|
||||||
|
border-top: 1px solid #999
|
||||||
|
|
||||||
|
.row
|
||||||
|
position: relative
|
||||||
|
|
||||||
|
p.no-margin
|
||||||
|
margin-bottom: 0
|
||||||
|
|
||||||
|
#ldapObjectTable table
|
||||||
|
width: 100%
|
4302
package-lock.json
generated
Normal file
4302
package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
29
package.json
Normal file
29
package.json
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
{
|
||||||
|
"name": "archon",
|
||||||
|
"version": "0.1.0",
|
||||||
|
"description": "A web-based LDAP user and group manager",
|
||||||
|
"main": "archon.js",
|
||||||
|
"scripts": {
|
||||||
|
"start": "node archon.js",
|
||||||
|
"test": "echo \"Error: no test specified\" && exit 1",
|
||||||
|
"grunt": "grunt"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "gitea@git.metaunix.net:metaunix/archon.git"
|
||||||
|
},
|
||||||
|
"author": "Gregory Ballantine <gballantine@metaunix.net>",
|
||||||
|
"license": "BSD-2-Clause",
|
||||||
|
"dependencies": {
|
||||||
|
"express": "^4.18.1",
|
||||||
|
"express-session": "^1.17.3",
|
||||||
|
"grunt-dart-sass": "^2.0.1",
|
||||||
|
"ldapjs": "^2.3.2",
|
||||||
|
"twig": "^1.15.4"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"grunt": "^1.5.3",
|
||||||
|
"grunt-contrib-coffee": "^2.1.0",
|
||||||
|
"grunt-contrib-watch": "^1.1.0"
|
||||||
|
}
|
||||||
|
}
|
0
public/.gitkeep
Normal file
0
public/.gitkeep
Normal file
32
routes/auth.js
Normal file
32
routes/auth.js
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
const ldap = require('ldapjs');
|
||||||
|
|
||||||
|
exports.getLogin = (req, res, next) => {
|
||||||
|
res.render('auth/login');
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.postLogin = (req, res, next) => {
|
||||||
|
|
||||||
|
bindHost = req.body.ldap_bind_host;
|
||||||
|
bindDn = req.body.ldap_bind_dn;
|
||||||
|
bindPw = req.body.ldap_bind_pw;
|
||||||
|
|
||||||
|
client = ldap.createClient({url: 'ldap://' + bindHost + '/'});
|
||||||
|
client.bind(bindDn, bindPw, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.log('There was an error while logging in. Please try again.');
|
||||||
|
res.redirect('/auth/login');
|
||||||
|
return next(err);
|
||||||
|
} else {
|
||||||
|
console.log('Success!');
|
||||||
|
req.session.ldap_bind_host = bindHost;
|
||||||
|
req.session.ldap_bind_dn = bindDn;
|
||||||
|
req.session.ldap_bind_pw = bindPw;
|
||||||
|
baseDnBits = bindDn.split(',');
|
||||||
|
baseDnBits.shift();
|
||||||
|
baseDn = baseDnBits.join(',');
|
||||||
|
req.session.ldap_base_dn = baseDn;
|
||||||
|
return res.redirect('/');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
31
routes/index.js
Normal file
31
routes/index.js
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
const ldap = require('ldapjs');
|
||||||
|
|
||||||
|
searchOpts = {
|
||||||
|
filter: '(objectClass=posixAccount)',
|
||||||
|
scope: 'sub',
|
||||||
|
attributes: ['uid', 'displayName', 'mail'],
|
||||||
|
};
|
||||||
|
|
||||||
|
exports.home = function(req, res, next) {
|
||||||
|
|
||||||
|
client = ldap.createClient({url: 'ldap://' + req.session.ldap_bind_host + '/'});
|
||||||
|
client.bind(req.session.ldap_bind_dn, req.session.ldap_bind_pw, (err) => {
|
||||||
|
if (err) {
|
||||||
|
console.log('There was an error while logging in. Please try again.');
|
||||||
|
res.redirect('/auth/login');
|
||||||
|
return next(err);
|
||||||
|
} else {
|
||||||
|
client.search('ou=People,' + req.session.ldap_base_dn, searchOpts, (err, result) => {
|
||||||
|
users = [];
|
||||||
|
result.on('searchEntry', (entry) => {
|
||||||
|
users.push(entry.object);
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.render('index', {
|
||||||
|
users: users
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
};
|
15
src/middleware/authMiddleware.js
Normal file
15
src/middleware/authMiddleware.js
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
exports.authProtected = (req, res, next) => {
|
||||||
|
|
||||||
|
// Extra check to make sure this isn't performed on the login page
|
||||||
|
doNotProtect = ['/auth/login'];
|
||||||
|
if (doNotProtect.includes(req.path)) {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!('ldap_bind_dn' in req.session)) {
|
||||||
|
return res.redirect('/auth/login');
|
||||||
|
} else {
|
||||||
|
return next();
|
||||||
|
}
|
||||||
|
|
||||||
|
};
|
38
views/auth/login.twig
Normal file
38
views/auth/login.twig
Normal file
@ -0,0 +1,38 @@
|
|||||||
|
{% extends 'layout.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<header id="header" class="row">
|
||||||
|
<div class="columns twelve">
|
||||||
|
<h1>Login</h1>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<form id="loginForm" class="columns twelve" action="/auth/login" method="POST">
|
||||||
|
<div class="row">
|
||||||
|
<label class="columns twelve">
|
||||||
|
LDAP Host:
|
||||||
|
<input class="u-full-width" type="text" name="ldap_bind_host" placeholder="Enter LDAP host...">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<label class="columns six">
|
||||||
|
Bind DN:
|
||||||
|
<input class="u-full-width" type="text" name="ldap_bind_dn" placeholder="Enter bind DN...">
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label class="columns six">
|
||||||
|
Bind Password:
|
||||||
|
<input class="u-full-width" type="password" name="ldap_bind_pw" placeholder="Enter bind password...">
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="row">
|
||||||
|
<div class="columns twelve u-text-center">
|
||||||
|
<input type="submit" name="bind_submit" value="Login">
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
{% endblock %}
|
42
views/index.twig
Normal file
42
views/index.twig
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
{% extends 'layout.twig' %}
|
||||||
|
|
||||||
|
{% block content %}
|
||||||
|
<header class="row">
|
||||||
|
<div class="columns twelve u-text-center">
|
||||||
|
<h1>Archon LDAP Manager</h1>
|
||||||
|
</div>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<section id="connectionInfo" class="row">
|
||||||
|
<div class="columns twelve u-text-center">
|
||||||
|
<p>Connected to: <span id="ldapHost"></span></p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="userActions" class="row">
|
||||||
|
<div class="columns twelve">
|
||||||
|
<p><a href="user/create.html">Create new user</a></p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<section id="ldapObjectTable" class="row">
|
||||||
|
<table>
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>User ID</th>
|
||||||
|
<th>Display Name</th>
|
||||||
|
<th>Email Address</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
<tbody id="ldapUserList">
|
||||||
|
{% for user in users %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ user.uid }}</td>
|
||||||
|
<td>{{ user.displayName }}</td>
|
||||||
|
<td>{{ user.mail }}</td>
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
{% endblock %}
|
32
views/layout.twig
Normal file
32
views/layout.twig
Normal file
@ -0,0 +1,32 @@
|
|||||||
|
<!DOCTYPE html>
|
||||||
|
<html lang="en">
|
||||||
|
<head>
|
||||||
|
<meta charset="UTF-8">
|
||||||
|
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/skeleton/2.0.4/skeleton.min.css">
|
||||||
|
<link rel="stylesheet" href="/styles/archon.css">
|
||||||
|
<title>Archon LDAP Manager</title>
|
||||||
|
<script src="/js/archon.js" charset="utf-8"></script>
|
||||||
|
{% block scripts %}{% endblock %}
|
||||||
|
</head>
|
||||||
|
<body>
|
||||||
|
<div class="container">
|
||||||
|
{% block content %}{% endblock %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<footer id="footer">
|
||||||
|
<div class="container fluid">
|
||||||
|
<div class="row">
|
||||||
|
<div class="columns three"><p></p></div>
|
||||||
|
<div class="columns six">
|
||||||
|
<p class="no-margin">This app was built using:</p>
|
||||||
|
<p class="no-margin">
|
||||||
|
Node.js <span id="node-version"></span>,
|
||||||
|
Chromium <span id="chrome-version"></span>,
|
||||||
|
and Electron <span id="electron-version"></span>.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</footer>
|
||||||
|
</body>
|
||||||
|
</html>
|
Reference in New Issue
Block a user