diff --git a/lib/app.js b/lib/app.js index f60304b0..aaf32f55 100644 --- a/lib/app.js +++ b/lib/app.js @@ -84,6 +84,12 @@ module.exports.initialize = function (config) { } return config.get('application').favicon.trim() }, + get topMenus () { + return [] + }, + get accountMenus () { + return [] + }, isAnonymous: function () { return !req.user diff --git a/lib/config.js b/lib/config.js index 23a97061..d38840d6 100644 --- a/lib/config.js +++ b/lib/config.js @@ -92,6 +92,11 @@ module.exports = (function () { passwordHash: '', email: '' }, + cas: { + enabled: false, + ssoBaseURL: '', + serverBaseURL: '' + }, local: { enabled: false, accounts: [{ @@ -167,6 +172,7 @@ module.exports = (function () { !config.authentication.github.enabled && !config.authentication.ldap.enabled && !config.authentication.alone.enabled && + !config.authentication.cas.enabled && !config.authentication.local.enabled ) { error = 'No authentication method provided.' diff --git a/package.json b/package.json index 69004509..007a5099 100644 --- a/package.json +++ b/package.json @@ -55,13 +55,15 @@ "morgan": "^1.5.0", "node-syntaxhighlighter": "*", "passport": "^0.2.0", + "passport-cas": "^0.1.1", "passport-github": "^0.1.5", "passport-google-oauth": "^0.1.5", "passport-local": "^1.0.0", + "pug": "^2.0.0-beta11", "semver": "^5.3.0", "serve-favicon": "^2.1.7", - "transliteration": "^0.2.1", - "pug": "^2.0.0-beta11" + "ssl-certs": "^1.0.0", + "transliteration": "^0.2.1" }, "devDependencies": { "chai": "*", diff --git a/public/css/style.css b/public/css/style.css index 6cb64bc6..0bcb99e7 100644 --- a/public/css/style.css +++ b/public/css/style.css @@ -27,6 +27,16 @@ a.logout i { padding-right: 0; } +.navbar a[href^="https://"], +.navbar a[href^="http://"] { + background: none; + padding-right: 0; +} + +.navbar li{ + margin-bottom: 0; +} + p.user { color: white; padding: 8px 0 0; @@ -247,8 +257,8 @@ hr { /* Overrides Bootstrap */ -.navbar-right { - float: right; +.navbar-brand{ + display: none; } .navbar-form { @@ -258,19 +268,18 @@ hr { .navbar-form.search { display: none; + margin-left: 15px; + margin-right: 15px; } -.navbar-header { - width: 100%; -} -.navbar-brand { - overflow: hidden; - max-width: 60%; +.navbar-inverse { + background-color: #24292e; } -.navbar-inverse { - background-color: rgba(0,0,0); +.navbar-inverse .navbar-nav>li>a{ + color: rgba(255, 255, 255, 0.75); + font-weight: bold; } .alert { @@ -539,6 +548,10 @@ input#pageTitle { margin-bottom: 20px; } +.search-xs input{ + border: 0; +} + .with-sidebar { background-color: rgba(255,255,255,0.5); padding: 10px; @@ -567,6 +580,10 @@ li { margin-bottom: 5px; } +.navbar-header li { + margin-bottom: 0px; +} + .jumbotron p.discrete { color: gray; font-size: 90% diff --git a/routes/auth.js b/routes/auth.js index 4182d68f..78e10928 100644 --- a/routes/auth.js +++ b/routes/auth.js @@ -111,6 +111,44 @@ if (auth.ldap.enabled) { )) } +if (auth.cas.enabled){ + require('ssl-certs'); + router.get('/cas_login', function(req, res, next) { + passport.authenticate('cas', function (err, user, info) { + if (err) { + console.error(err); + return next(err); + } + if (!user) { + return next(new Error('cas login failed')); + } + req.logIn(user, function (err) { + if (err) { + return next(err); + } + return res.redirect(proxyPath + '/auth/done'); + }); + })(req, res, next); + }); + + passport.use(new(require('passport-cas').Strategy)({ + version: 'CAS3.0', + serviceURL: '/cas_login', + ssoBaseURL: auth.cas.ssoBaseURL, + serverBaseURL: auth.cas.serverBaseURL, + validateURL: '/serviceValidate' // for CAS 2.0 + }, function(profile, done) { + console.log('cas login', profile.user); + usedAuthentication('cas'); + return done(null, { + displayName: profile.attributes && (profile.attributes.displayName || profile.user), + email: profile.attributes && (profile.attributes.email || profile.user+'@saas-plat.com') + }); + }) + ); + +} + if (auth.alone.enabled) { passport.use(new passportLocal.Strategy( @@ -234,6 +272,10 @@ function _getLogin (req, res) { res.locals.errors = req.flash() + if (auth.cas.enabled){ + return res.redirect('cas_login'); + } + res.render('login', { title: app.locals.config.get('application').title, auth: auth diff --git a/views/layout.pug b/views/layout.pug index 5b11e651..f45c31e7 100644 --- a/views/layout.pug +++ b/views/layout.pug @@ -22,46 +22,69 @@ include mixins/links body -var term_ph = (typeof term == "undefined" ? "" : term) .navbar.navbar-inverse.navbar-fixed-top - .container-fluid + .container-fluid .navbar-header +anchor("/", appBrand).navbar-brand - if canSearch() - form(action=`${proxyPath}/search`).navbar-form.search.navbar-left - .input-group.input-group-sm.search - input.form-control(type="text", value=term_ph, data-i-search-input="true",name="term",placeholder="Search the wiki") - span.input-group-btn - button.btn.btn-primary(type="submit") Search - .navbar-right - if isAnonymous() - p.user You're not  - +anchor('/login?destination', 'logged in')#login(title='Access login page') - else - p.user - if hasGravatar() - img(src=gravatar().url(user.email, {s:24})) - b  #{user.displayName}  - +anchor('/logout')(title='Become anonymous') - i.icon.ion-power + button(type="button",data-toggle="collapse" data-target="#navbar-collapse-1" aria-expanded="false").navbar-toggle.collapsed + span.sr-only Toggle navigation + span.icon-bar + span.icon-bar + span.icon-bar + .collapse.navbar-collapse(id="navbar-collapse-1") + if topMenus.length>0 + ul.nav.navbar-nav + each m in topMenus + li + +anchor(m.url, m.text) + if canSearch() + form(action=`${proxyPath}/search`).navbar-form.search.navbar-left + .input-group.input-group-sm.search + input.form-control(type="text", value=term_ph, data-i-search-input="true",name="term",placeholder="Search the wiki") + span.input-group-btn + button.btn.btn-primary(type="submit") Search + ul.nav.navbar-nav.navbar-right + li.dropdown + if isAnonymous() + +anchor('/login?destination', 'logged in')#login(title='Access login page') + else + a(href="#",class="dropdown-toggle user",data-toggle="dropdown",role="button",aria-haspopup="true",aria-expanded="false") + if hasGravatar() + img(src=gravatar().url(user.email, {s:24})) + span #{user.displayName} + span.caret + ul.dropdown-menu + each am in accountMenus + li + +anchor(am.url, am.text) + li + a(href="/logout",title='Become anonymous') + i.icon.ion-power + span Logout + .tools block tools - .container + if canSearch() + form(action=`${proxyPath}/search`).visible-xs-block.search-xs + input.form-control(type="text", value=term_ph, data-i-search-input="true",name="term",placeholder="Search the wiki") + .container + .row + #main.hide-tools.col-md-9 + block content + if hasSidebar() - .col-md-2.with-sidebar + .col-md-3.with-sidebar .content !{_sidebar} else - .col-md-2 - - #main.hide-tools.col-md-8 - block content + .col-md-3 if hasFooter() .row - .col-md-2 - .col-md-8.with-footer + .col-md-3 + .col-md-9.with-footer .content !{_footer} script(src=proxyPath + "/vendor/jquery.min.js") diff --git a/views/search.pug b/views/search.pug index bcdf9b93..5b16aeca 100644 --- a/views/search.pug +++ b/views/search.pug @@ -11,7 +11,7 @@ block content +warning() if canSearch() - form(action=`${proxyPath}/search`).search-form + form(action=`${proxyPath}/search`).search-form.hidden-xs .input-group.input-group-sm input.form-control(type="text", value=term_ph, data-i-search-input="true", name="term", placeholder="Search the wiki") span.input-group-btn