зеркало из
1
0
Форкнуть 0

Active Directory primary athentication support

This commit is contained in:
Jeff Wilcox 2016-07-15 08:05:01 -07:00
Родитель 5964b974db
Коммит 2937a97ffe
20 изменённых файлов: 454 добавлений и 284 удалений

6
app.js
Просмотреть файл

@ -20,7 +20,11 @@ app.initializeApplication = function init(config, configurationError, callback)
};
}
app.set('runtimeConfig', config);
require('./middleware/')(app, express, config, __dirname, redisClient, error);
try {
require('./middleware/')(app, express, config, __dirname, redisClient, error);
} catch (middlewareError) {
error = middlewareError;
}
if (!error) {
app.use('/', require('./routes/'));
}

Просмотреть файл

@ -48,7 +48,7 @@ module.exports = function translateEnvironmentToConfiguration(legacyConfiguratio
websiteSku: configurationHelper.get('WEBSITE_SKU'),
expectedSslCertificate: configurationHelper.get('EXPECTED_SSL_CERTIFICATE'),
allowHttp: configurationHelper.get('DEBUG_ALLOW_HTTP'),
showDebugFooter: configurationHelper.get('DEBUG_SHOW_FOOTER'),
showDebugFooter: (configurationHelper.get('DEBUG_SHOW_FOOTER') === true || configurationHelper.get('DEBUG_SHOW_FOOTER') === 'true'),
corporate: {
userProfilePrefix: configurationHelper.get('CORPORATE_PROFILE_PREFIX'),
trainingResources: require('./resources.json'),
@ -61,6 +61,7 @@ module.exports = function translateEnvironmentToConfiguration(legacyConfiguratio
cla: utils.arrayFromString(configurationHelper.get('FRIENDS_CLA')),
employeeData: utils.arrayFromString(configurationHelper.get('FRIENDS_DATA')),
},
primaryAuthenticationScheme: configurationHelper.get('PRIMARY_AUTHENTICATION_SCHEME'),
// GitHub application properties and secrets
github: {
clientId: configurationHelper.get('GITHUB_CLIENT_ID'),

14
data.js
Просмотреть файл

@ -223,7 +223,7 @@ DataClient.prototype.removeError = function (partitionKey, rowKey, callback) {
DataClient.prototype.getActiveErrors = function (correlationId, callback) {
var dc = this;
// Correlation ID is optional
if (typeof (correlationId) == 'function') {
if (typeof (correlationId) === 'function') {
callback = correlationId;
correlationId = undefined;
}
@ -305,7 +305,7 @@ DataClient.prototype.mergeIntoEntity = function mit(entity, obj, callback) {
DataClient.prototype.createEntity = function ce(partitionKey, rowKey, obj, callback) {
var dc = this;
if (typeof (obj) == 'function') {
if (typeof (obj) === 'function') {
callback = obj;
obj = undefined;
}
@ -385,7 +385,7 @@ DataClient.prototype.getUserLinkByUsername = function gulbyu(githubUsername, cal
this.getUserLinkByProperty('ghu', githubUsername, function (error, data) {
if (error) return callback(error);
if (data && data.length) {
if (data.length == 1) {
if (data.length === 1) {
callback(null, data[0]);
} else {
if (data.length === 0) {
@ -527,12 +527,12 @@ DataClient.prototype.getPendingApprovals = function getPendingApprovals(teamsIn,
var dc = this;
var teams = null;
var i;
if (typeof teamsIn == 'number') {
if (typeof teamsIn === 'number') {
teams = [teamsIn.toString()];
}
else if (typeof teamsIn == 'string') {
else if (typeof teamsIn === 'string') {
teams = [teamsIn];
} else if (typeof teamsIn == 'function') {
} else if (typeof teamsIn === 'function') {
callback = teamsIn;
teams = []; // Special case: empty list means all pending approvals
} else {
@ -611,7 +611,7 @@ DataClient.prototype.getApprovalRequest = function gar(requestId, callback) {
DataClient.prototype.getPendingApprovalsForUserId = function gpeaf(githubid, callback) {
var dc = this;
if (typeof githubid == 'number') {
if (typeof githubid === 'number') {
githubid = githubid.toString();
}
var query = new azure.TableQuery()

Просмотреть файл

@ -14,6 +14,7 @@ module.exports = function configureErrorRoutes(app, initializationError) {
app.use((req, res, next) => {
var error = new Error('Application initialization error');
error.detailed = initializationError.message || null;
error.innerError = initializationError;
return next(error);
});
}

Просмотреть файл

@ -9,50 +9,54 @@ const bodyParser = require('body-parser');
const compression = require('compression');
module.exports = function initMiddleware(app, express, config, dirname, redisClient, initializationError) {
if (!initializationError) {
if (config.allowHttp) {
console.warn('WARNING: Allowing HTTP for local debugging');
} else {
app.use(require('./sslify'));
app.use(require('./hsts'));
}
require('./appInsights')(config);
if (!initializationError) {
if (config.allowHttp) {
console.warn('WARNING: Allowing HTTP for local debugging');
} else {
app.use(require('./sslify'));
app.use(require('./hsts'));
}
require('./appInsights')(config);
}
app.set('views', path.join(dirname, 'views'));
app.set('view engine', 'jade');
app.set('view cache', false);
app.set('views', path.join(dirname, 'views'));
app.set('view engine', 'jade');
app.set('view cache', false);
app.use(favicon(dirname + '/public/favicon.ico'));
app.use(favicon(dirname + '/public/favicon.ico'));
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(compression());
app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: false }));
app.use(compression());
var passport;
if (!initializationError) {
app.use(require('./session')(config, redisClient));
try {
passport = require('./passport-config')(app, config);
} catch (passportError) {
initializationError = passportError;
}
var passport;
if (!initializationError) {
app.use(require('./session')(config, redisClient));
try {
passport = require('./passport-config')(app, config);
} catch (passportError) {
initializationError = passportError;
}
}
app.use(express.static(path.join(dirname, 'public')));
app.use(express.static(path.join(dirname, 'public')));
app.use(require('./scrubbedUrl'));
app.use(require('./logger'));
if (!initializationError && config.websiteSku && !config.allowHttp) {
app.use(require('./requireSecureAppService'));
app.use(require('./scrubbedUrl'));
app.use(require('./logger'));
if (!initializationError && config.websiteSku && !config.allowHttp) {
app.use(require('./requireSecureAppService'));
}
app.use(require('./correlationId'));
app.use(require('./locals'));
if (!initializationError) {
require('./passport-routes')(app, passport, config);
if (config.onboarding && config.onboarding.length && config.onboarding.length > 0) {
require('./onboarding')(app, config);
}
app.use(require('./correlationId'));
app.use(require('./locals'));
}
if (!initializationError) {
require('./passport-routes')(app, passport);
if (config.onboarding && config.onboarding.length && config.onboarding.length > 0) {
require('./onboarding')(app, config);
}
}
if (initializationError) {
throw initializationError;
}
};

Просмотреть файл

@ -3,54 +3,72 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
const passport = require('passport');
'use strict';
const passport = require('passport');
const utils = require('../utils');
const GitHubStrategy = require('passport-github').Strategy;
const OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
function githubResponseToSubset(accessToken, refreshToken, profile, done) {
let subset = {
github: {
accessToken: accessToken,
avatarUrl: profile._json && profile._json.avatar_url ? profile._json.avatar_url : undefined,
displayName: profile.displayName,
id: profile.id,
profileUrl: profile.profileUrl,
username: profile.username,
}
};
return done(null, subset);
};
function activeDirectorySubset(iss, sub, profile, accessToken, refreshToken, done) {
// CONSIDER: TODO: Hybrid tenant checks.
// CONSIDER: Should check for existance of UPN, OID
let subset = {
azure: {
displayName: profile.displayName,
oid: profile._json.oid,
username: profile._json.upn,
token: {
access: accessToken,
refresh: refreshToken,
exp: profile._json.exp,
},
}
};
done(null, subset);
}
module.exports = function (app, config) {
if (!config.primaryAuthenticationScheme) {
config.primaryAuthenticationScheme = 'github';
}
if (config.primaryAuthenticationScheme !== 'github' && config.primaryAuthenticationScheme !== 'aad') {
throw new Error(`Unsupported primary authentication scheme type "${primaryAuthenticationScheme}"`);
}
// ----------------------------------------------------------------------------
// GitHub Passport session setup.
// ----------------------------------------------------------------------------
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing. However, since this example does not
// have a database of user records, the complete GitHub profile is serialized
// and deserialized.
passport.serializeUser(function(user, done) {
done(null, user);
});
passport.deserializeUser(function(obj, done) {
done(null, obj);
});
var gitHubTokenToSubset = function (accessToken, refreshToken, profile, done) {
var subset = {
github: {
accessToken: accessToken,
avatarUrl: profile._json && profile._json.avatar_url ? profile._json.avatar_url : undefined,
displayName: profile.displayName,
id: profile.id,
profileUrl: profile.profileUrl,
username: profile.username,
}
};
return done(null, subset);
};
passport.use(new GitHubStrategy({
let githubOptions = {
clientID: config.github.clientId,
clientSecret: config.github.clientSecret,
callbackURL: config.github.callbackUrl,
scope: [],
userAgent: 'passport-azure-oss-portal-for-github' // CONSIDER: User agent should be configured.
}, gitHubTokenToSubset));
// ----------------------------------------------------------------------------
// Azure Active Directory Passport session setup.
// ----------------------------------------------------------------------------
var aadStrategy = new OIDCStrategy({
};
let githubPassportStrategy = new GitHubStrategy(githubOptions, githubResponseToSubset);
let aadStrategy = new OIDCStrategy({
callbackURL: config.activeDirectory.redirectUrl,
realm: config.activeDirectory.tenantId,
clientID: config.activeDirectory.clientId,
@ -61,21 +79,28 @@ module.exports = function (app, config) {
responseType: 'id_token code',
responseMode: 'form_post',
validateIssuer: true,
}, function (iss, sub, profile, accessToken, refreshToken, done) {
done(null, profile);
});
}, activeDirectorySubset);
// Validate the authorization URL borrowed from the passport-github library
if (githubPassportStrategy._oauth2 && githubPassportStrategy._oauth2._authorizeUrl) {
app.set('runtime/passport/github/authorizeUrl', githubPassportStrategy._oauth2._authorizeUrl);
} else {
throw new Error('The GitHub Passport strategy library may have been updated, it no longer contains the expected Authorize URL property within the OAuth2 object.');
}
passport.use('github', githubPassportStrategy);
passport.use('azure-active-directory', aadStrategy);
// ----------------------------------------------------------------------------
// Expanded OAuth-scope GitHub access for org membership writes.
// ----------------------------------------------------------------------------
var expandedGitHubScopeStrategy = new GitHubStrategy({
let expandedGitHubScopeStrategy = new GitHubStrategy({
clientID: config.github.clientId,
clientSecret: config.github.clientSecret,
callbackURL: config.github.callbackUrl + '/increased-scope',
scope: ['write:org'],
userAgent: 'passport-azure-oss-portal-for-github' // CONSIDER: User agent should be configured.
}, gitHubTokenToSubset);
}, githubResponseToSubset);
passport.use('expanded-github-scope', expandedGitHubScopeStrategy);

Просмотреть файл

@ -3,104 +3,148 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
module.exports = function configurePassport(app, passport) {
// ----------------------------------------------------------------------------
// passport integration with GitHub
// ----------------------------------------------------------------------------
app.get('/signin/github', function (req, res) {
if (req.session && req.headers && req.headers.referer) {
req.session.referer = req.headers.referer;
}
return res.redirect('/auth/github');
});
'use strict';
app.get('/auth/github',
passport.authenticate('github'),
function (req, res){
// The request will be redirected to GitHub for authentication, so this
// function will not be called.
});
const querystring = require('querystring');
app.get('/auth/github/callback',
passport.authenticate('github', { failureRedirect: '/failure/github' }),
function (req, res) {
var url = '/';
if (req.session && req.session.referer) {
url = req.session.referer;
delete req.session.referer;
}
res.redirect(url);
});
module.exports = function configurePassport(app, passport, initialConfig) {
// ----------------------------------------------------------------------------
// passport integration with GitHub
// ----------------------------------------------------------------------------
app.get('/signin/github', function (req, res) {
if (req.session && req.headers && req.headers.referer) {
req.session.referer = req.headers.referer;
}
return res.redirect('/auth/github');
});
app.get('/signout', function (req, res) {
req.logout();
res.redirect('https://github.com/logout');
});
var ghMiddleware = initialConfig.primaryAuthenticationScheme === 'github' ? passport.authenticate('github') : passport.authorize('github');
// ----------------------------------------------------------------------------
// Expanded GitHub auth scope routes
// ----------------------------------------------------------------------------
app.get('/signin/github/increased-scope', function (req, res){
if (req.session && req.headers && req.headers.referer) {
req.session.referer = req.headers.referer;
}
return res.redirect('/auth/github/increased-scope');
});
app.get('/auth/github', ghMiddleware);
app.get('/auth/github/increased-scope', passport.authorize('expanded-github-scope'));
app.get('/auth/github/callback', ghMiddleware, (req, res) => {
if (initialConfig.primaryAuthenticationScheme !== 'github') {
req.user.github = req.account.github;
}
var url = '/';
if (req.session && req.session.referer) {
url = req.session.referer;
delete req.session.referer;
}
res.redirect(url);
});
app.get('/auth/github/callback/increased-scope',
passport.authorize('expanded-github-scope'), function (req, res, next) {
var account = req.account;
var user = req.user;
user.github.increasedScope = account;
var url = '/';
if (req.session && req.session.referer) {
url = req.session.referer;
delete req.session.referer;
}
res.redirect(url);
});
// ----------------------------------------------------------------------------
// passport integration with Azure Active Directory
// ----------------------------------------------------------------------------
app.get('/auth/azure', passport.authorize('azure-active-directory'));
app.post('/auth/azure/callback',
passport.authorize('azure-active-directory'), function (req, res, next) {
var account = req.account;
var username = account._json.upn;
if (account !== null && username && account.displayName) {
req.user.azure = {
displayName: account.displayName,
oid: account._json.oid,
username: username,
};
var url = '/';
if (req.session && req.session.referer) {
url = req.session.referer;
delete req.session.referer;
}
return res.redirect(url);
} else {
return next(new Error('Azure Active Directory authentication failed.'));
}
if (initialConfig.primaryAuthenticationScheme === 'aad') {
app.get('/signin/github/join', (req, res, next) => {
res.render('creategithubaccount', {
title: 'Create a GitHub account',
});
app.get('/signin/azure', function(req, res){
if (req.session && req.headers && req.headers.referer) {
if (req.session.referer === undefined) {
req.session.referer = req.headers.referer;
}
}
return res.redirect('/auth/azure');
});
app.get('/signout/azure', function(req, res){
if (req.user && req.user.azure) {
delete req.user.azure;
}
res.redirect('/');
app.get('/auth/github/join', (req, res, next) => {
var config = req.app.settings.runtimeConfig;
var authorizeRelativeUrl = req.app.settings['runtime/passport/github/authorizeUrl'].replace('https://github.com', '');
var joinUrl = 'https://github.com/join?' + querystring.stringify({
return_to: `${authorizeRelativeUrl}?` + querystring.stringify({
client_id: config.github.clientId,
redirect_uri: config.github.callbackUrl,
response_type: 'code',
scope: '', // TODO: Improve by pulling from object?
}),
source: 'oauth',
});
res.redirect(joinUrl);
});
}
app.get('/signout', function (req, res) {
var config = req.app.settings.runtimeConfig;
req.logout();
if (config.primaryAuthenticationScheme === 'github') {
res.redirect('https://github.com/logout');
} else {
res.render('message', {
messageTitle: 'Goodbye!',
message: 'You have been signed out.',
buttonText: 'Sign In Again',
});
}
});
app.get('/signout/github', function (req, res) {
if (req.app.settings.runtimeConfig.primaryAuthenticationScheme === 'github') {
return res.redirect('/signout');
}
if (req.user && req.user.github) {
delete req.user.github;
}
var url = req.headers.referer || '/';
res.redirect(url);
});
// ----------------------------------------------------------------------------
// Expanded GitHub auth scope routes
// ----------------------------------------------------------------------------
app.get('/signin/github/increased-scope', function (req, res){
if (req.session && req.headers && req.headers.referer) {
req.session.referer = req.headers.referer;
}
return res.redirect('/auth/github/increased-scope');
});
// TODO: xxx
app.get('/auth/github/increased-scope', passport.authorize('expanded-github-scope'));
// TODO: xxx
app.get('/auth/github/callback/increased-scope',
passport.authorize('expanded-github-scope'), function (req, res, next) {
var account = req.account;
var user = req.user;
user.github.increasedScope = account;
var url = '/';
if (req.session && req.session.referer) {
url = req.session.referer;
delete req.session.referer;
}
res.redirect(url);
});
// ----------------------------------------------------------------------------
// passport integration with Azure Active Directory
// ----------------------------------------------------------------------------
var aadMiddleware = initialConfig.primaryAuthenticationScheme === 'github' ? passport.authorize('azure-active-directory') : passport.authenticate('azure-active-directory');
app.get('/auth/azure', aadMiddleware);
app.post('/auth/azure/callback', aadMiddleware, (req, res, next) => {
if (initialConfig.primaryAuthenticationScheme !== 'aad') {
req.user.azure = req.account.azure;
}
var url = '/';
if (req.session && req.session.referer) {
url = req.session.referer;
delete req.session.referer;
}
return res.redirect(url);
});
app.get('/signin/azure', function(req, res){
if (req.session && req.headers && req.headers.referer) {
if (req.session.referer === undefined) {
req.session.referer = req.headers.referer;
}
}
return res.redirect('/auth/azure');
});
app.get('/signout/azure', function(req, res){
if (req.app.settings.runtimeConfig.primaryAuthenticationScheme === 'aad') {
return res.redirect('/signout');
}
if (req.user && req.user.azure) {
delete req.user.azure;
}
res.redirect('/');
});
};

Просмотреть файл

@ -24,6 +24,10 @@ function OpenSourceUserContext (applicationConfiguration, dataClient, user, redi
this.modernUser = function () {
return modernUser;
};
this.createModernUser = function (id, login) {
modernUser = new User(this, id);
modernUser.login = login;
}
this.setting = function (name) {
return applicationConfiguration[name];
};
@ -53,14 +57,13 @@ function OpenSourceUserContext (applicationConfiguration, dataClient, user, redi
github: user && user.github && user.github.id ? user.github.id.toString() : undefined,
};
if (this.id.github) {
modernUser = new User(this, this.id.github);
modernUser.login = this.usernames.github;
this.createModernUser(this.id.github, this.usernames.github);
}
this.baseUrl = '/';
this.redis = new RedisHelper(this, applicationConfiguration.redis.prefix);
this.initializeBasics(function () {
this.initializeBasics(function (initError) {
if (callback) {
return callback(null, self);
return callback(initError, self);
}
});
}
@ -69,11 +72,38 @@ function OpenSourceUserContext (applicationConfiguration, dataClient, user, redi
// Populate the user's OSS context object.
// ----------------------------------------------------------------------------
OpenSourceUserContext.prototype.initializeBasics = function (callback) {
var self = this;
var requestUser = this.requestUser();
if (!userObject && this.setting('primaryAuthenticationScheme') === 'aad' && requestUser.azure && requestUser.azure.username) {
return this.dataClient().getUserByAadUpn(requestUser.azure.username, function (findError, userLinks) {
if (findError) {
// XXX: wrap with a useful message?
return callback(findError);
}
if (userLinks.length === 0) {
return callback();
}
if (userLinks.length > 1) {
var tooManyLinksError = new Error(`This account has ${userLinks.length} linked GitHub accounts.`);
tooManyLinksError.links = userLinks;
tooManyLinksError.tooManyLinks = true;
return callback(tooManyLinksError);
}
var link = userLinks[0];
self.usernames.github = link.ghu;
self.id.github = link.ghid.toString();
self.createModernUser(self.id.github, self.usernames.github);
self.entities.link = link;
self.modernUser().link = link;
// todo: if their AAD name or upn has changed, but oid is still the same... schedule an update!
// question: should this.authenticated.github be true or false, since it isn't authenticated yet?
callback(null, false);
});
}
var userObject = this.modernUser();
if (!userObject) {
return callback(new Error("There's a logic bug in the user context object. We cannot continue."));
}
var self = this;
userObject.getLink(function (error, link) {
if (error) {
return callback(utils.wrapError(error, 'We were not able to retrieve information about any link for your user account at this time.'));

Просмотреть файл

@ -129,7 +129,7 @@ h1.huge {
h2 strong {
font-family: "Segoe UI Semibold", Segoe, "Segoe WP", Calibri, Candara, Tahoma, Verdana, Arial, sans-serif;
font-weight: normal;
font-weight: normal;
}
/* custom color button for a more muted appearance */
@ -184,6 +184,10 @@ h2 strong {
border-color: #0078d7;
}
.full-width {
width: 100%;
}
/* wiki editing tools */
textarea#editor-body {
margin-top: 24px;

Просмотреть файл

@ -3,27 +3,31 @@
// Licensed under the MIT license. See LICENSE file in the project root for full license information.
//
var express = require('express');
var router = express.Router();
var async = require('async');
var moment = require('moment');
var utils = require('../utils');
var OpenSourceUserContext = require('../oss');
var linkRoute = require('./link');
var linkedUserRoute = require('./index-linked');
const express = require('express');
const router = express.Router();
const async = require('async');
const moment = require('moment');
const utils = require('../utils');
const OpenSourceUserContext = require('../oss');
const linkRoute = require('./link');
const linkedUserRoute = require('./index-linked');
router.use(function (req, res, next) {
var config = req.app.settings.runtimeConfig;
if (req.isAuthenticated()) {
if (req.user && !req.user.github) {
// no github info
// IF secondary auth,... 1) check for a link
// return next(new Error('We do not have your GitHub stuff.'));
}
if (req.user && req.user.github && !req.user.github.id) {
return next(new Error('Invalid GitHub user information provided by GitHub.'));
}
var config = req.app.settings.runtimeConfig;
var dc = req.app.settings.dataclient;
var instance = new OpenSourceUserContext(config, dc, req.user, dc.cleanupInTheFuture.redisClient, function (error) {
var unused = new OpenSourceUserContext(config, dc, req.user, dc.cleanupInTheFuture.redisClient, function (error, instance) {
req.oss = instance;
instance.addBreadcrumb(req, 'Organizations');
return next();
return next(error);
});
} else {
var url = req.originalUrl;
@ -32,7 +36,8 @@ router.use(function (req, res, next) {
req.session.referer = req.originalUrl;
}
}
res.redirect('/auth/github');
var authUrl = config.primaryAuthenticationScheme === 'github' ? '/auth/github' : '/auth/azure';
res.redirect(authUrl);
}
});
@ -45,16 +50,24 @@ router.get('/', function (req, res, next) {
var config = req.app.settings.runtimeConfig;
var onboarding = req.query.onboarding !== undefined;
var allowCaching = onboarding ? false : true;
if (!link && req.user.azure === undefined) {
return oss.render(req, res, 'welcome', 'Welcome');
}
if (!link && req.user.azure && req.user.azure.oid) {
return res.redirect('/link');
if (!link) {
if (config.primaryAuthenticationScheme === 'github' && req.user.azure === undefined ||
config.primaryAuthenticationScheme === 'aad' && req.user.github === undefined) {
return oss.render(req, res, 'welcome', 'Welcome');
}
if (config.primaryAuthenticationScheme === 'github' && req.user.azure && req.user.azure.oid ||
config.primaryAuthenticationScheme === 'aad' && req.user.github && req.user.github.id) {
return res.redirect('/link');
}
return next(new Error('This account is not yet linked, but a workflow error is preventing further progress. Please report this issue. Thanks.'));
}
// They're changing their corporate identity (rare, often just service accounts)
if (link && link.aadupn && req.user.azure && req.user.azure.username && req.user.azure.username.toLowerCase() !== link.aadupn.toLowerCase()) {
return res.redirect('/link/update');
}
// TODO: Add similar code for GitHub data, etc.
var twoFactorOff = null;
var activeOrg = null;
async.parallel({

Просмотреть файл

@ -58,9 +58,7 @@ router.post('/', function (req, res, next) {
if (error) {
return next(utils.wrapError(error, 'You were successfully removed from all of your organizations. However, a minor failure happened during a data housecleaning operation. Double check that you are happy with your current membership status on GitHub.com before continuing. Press Report Bug if you would like this handled for sure.'));
}
delete req.user.azure;
req.logout();
res.redirect('/');
res.redirect('/signout');
});
});
});

Просмотреть файл

@ -0,0 +1,24 @@
//-
//- Copyright (c) Microsoft. All rights reserved.
//- Licensed under the MIT license. See LICENSE file in the project root for full license information.
//-
extends layout
block content
div.container
h2 Create your GitHub account username
p.
Once you have created your account and verified your e-mail address,
please come back to this repository management site to continue your
onboarding experience.
hr
div.row
div.col-sm-6.col-md-6.col-lg-6
a.btn.btn-primary.full-width(href='/auth/github/join')
h3 I'm new to GitHub
p Let's create a brand new GitHub username
div.col-sm-6.col-md-6.col-lg-6
p.lead Since GitHub is a third-party service, it has its own set of usernames, passwords, multi-factor authentication, and settings.

Просмотреть файл

@ -84,12 +84,12 @@ html(lang="en")
img(alt=user.github.displayName, src=user.github.avatarUrl + '&s=80', style='margin-right:10px;width:30px;height:30px', data-user=user.github.id)
a.btn.btn-sm.btn-muted(href='https://github.com/settings/profile', target='_new', title='Click to edit your public GitHub profile')= user.github.username
a.btn.btn-sm.btn-muted-more(href='https://github.com/settings/profile', target='_new', title='Click to edit your public GitHub profile')= user.github.displayName || user.github.username
a.btn.btn-sm.btn-white(href='/signout', style='margin-left:10px') Sign out
a.btn.btn-sm.btn-white(href='/signout', style='margin-left:10px') Sign Out
else
p
small Sign in or create your GitHub.com account to manage your #{config && config.companyName || 'corporate'} open source identity.
p
a.btn.btn-primary(href='/signin/github') Sign in
a.btn.btn-sm.btn-primary(href='/signin/github') Sign in
div.col-md-6
if user && !error
if ossLink
@ -143,6 +143,9 @@ html(lang="en")
h4 Hostname: #{serverName}
if config && config.applicationInsights && config.applicationInsights.instrumentationKey
h4 AppInsights: Instrumented
if config && config.primaryAuthenticationScheme
h4
| Primary Authentication: #{config.primaryAuthenticationScheme === 'github' ? 'GitHub' : 'Azure Active Directory'}
div.col-md-6.col-lg-6(style='background-color:white')
if config && config.azureStorage
h4

Просмотреть файл

@ -14,26 +14,34 @@ block content
div.row
div.col-md-7.col-lg-7
h1 Link your accounts
p Linking associates your corporate and social coding accounts to enable self-service GitHub management. It does not alter your GitHub account in any way.
p.lead.
Enable self-service open source management by linking
your corporate & social coding accounts
table.table
thead
tr
th GitHub User
th #{config.companyName} Identity
th GitHub Username
th
th #{config.companyName} Directory
tbody
tr
td= user.github.displayUsernameTemporary || user.github.username
td= user.azure.username
- gitAlertClass = config.primaryAuthenticationScheme == 'github' ? '' : 'alert-success'
- aadAlertClass = config.primaryAuthenticationScheme == 'aad' ? '' : 'alert-success'
td(class=gitAlertClass)= user.github.displayUsernameTemporary || user.github.username
td
div.text-center
i.glyphicon.glyphicon-link
td(class=aadAlertClass)= user.azure.username
p By continuing, you agree:
ul
li My GitHub account is controlled exclusively by #{user.azure.username}.
li My GitHub password is safe, secure and smart.
li I will enable two-factor authentication on my GitHub account and keep it active. I understand that I will lose access if I remove this security protection.
li My GitHub account is controlled by #{user.azure.username}
li My GitHub password is safe and secure
li I will enable two-factor authentication on my GitHub account
form(method='post')
p(style='margin-top:24px')
input.btn.btn-primary(type='submit', value='I Agree')
input.btn.btn-lg.btn-primary(type='submit', value='Link')
|    
a.btn.btn-default(href='/signout') Cancel
a.btn.btn-lg.btn-default(href='/signout') Cancel
hr
h3 Your onboarding progress
h5
@ -42,7 +50,7 @@ block content
h5.text-primary
| Link your identity
h5
| Join and accept your first organization invite from GitHub
| Join your first GitHub organization
h5
| Multifactor security checkup
h5
@ -58,8 +66,7 @@ block content
- var footres = config.corporate.trainingResources.footer
if footres
// These same resources appear on every single auth page footer, too.
h3 Training & Resources
p Bookmark these great resources today. These are important resources to grok.
h3 Open Source Resources
each categoryList, category in footres
h5= category
ul

Просмотреть файл

@ -26,9 +26,10 @@ block content
pre= messageOutput
p
a.btn.btn-default(href='/') Home
br
a.btn.btn-default(href='/')= buttonText || 'Home'
if config && config.corporate && config.corporate.portalAdministratorEmail
p
| If you have any questions, please ping  
a.alert-link(href='mailto:' + config.corporate.portalAdministratorEmail)= config.corporate.portalAdministratorEmail
p
| If you have any questions, please ping  
a.alert-link(href='mailto:' + config.corporate.portalAdministratorEmail)= config.corporate.portalAdministratorEmail

Просмотреть файл

@ -13,8 +13,7 @@ block content
div.container
h1
span.capitalize= org.name
|
span.capitalize= org.name + ' '
small Organization
div.container
@ -147,7 +146,7 @@ block content
a.btn.btn-default.btn-sm(href=team.org.baseUrl + 'leave') Leave Organization
else
a.btn.btn-default.btn-sm(href='https://github.com/orgs/' + team.org.name + '/teams/' + team.slug, target='_new')
| Open on GitHub
| Open on GitHub
i.glyphicon.glyphicon-share-alt
else
//-p You are not currently a member of any GitHub teams that grant you permission to specific repositories. You may be pre-approved to join teams.
@ -167,7 +166,7 @@ block content
h4= orgUser.login
p(style='margin-top:18px')
a.btn.btn-sm.btn-muted(href='https://github.com/' + org.name, target='_new')
| View on GitHub
| View on GitHub
i.glyphicon.glyphicon-share-alt
div.col-md-8.col-lg-8.col-md-offset-1.col-lg-offset-1
div.row
@ -185,16 +184,15 @@ block content
h6 On the Web
p
a(href=orgUser.otherFields.blog, target='_new')
= orgUser.otherFields.blog
|
= orgUser.otherFields.blog + ' '
i.glyphicon.glyphicon-share-alt
if orgUser.getProfileCreatedDate()
h6 Created
p
p
time(datetime=orgUser.getProfileCreatedDate().toISOString())= orgUser.getProfileCreatedDate().toDateString()
if orgUser.getProfileCreatedDate()
h6 Updated
p
p
time(datetime=orgUser.getProfileUpdatedDate().toISOString())= orgUser.getProfileUpdatedDate().toDateString()
hr
if org.inner.settings.organizationPurpose
@ -231,7 +229,7 @@ block content
small Remaining Private Repos
h2
| ∞ 
small Remaining OSS Repos
small Remaining Public Repos
if org.inner.settings.trainingResources
- var tr = org.inner.settings.trainingResources

Просмотреть файл

@ -15,12 +15,12 @@ block content
if state == 'pending'
h1 You've been invited!
h1 Action Required: You've been manually invited!
p.lead We've just had GitHub send you an invitation to <em>#{org.name}</em>.
p.lead GitHub has sent an invitation to <em>#{org.name}</em>.
p
| For security purposes, GitHub requires you to accept the invitation directly on their web site.
strong You must come back to this tab after accepting your invitation to continue setting up your open source teams and permissions.
| For security purposes, GitHub requires you to accept the invitation directly on their web site.&nbsp;
strong You must come back to this tab after accepting your invitation to continue setting up your open source teams and permissions.
div.row
div.col-md-6.col-lg-6
h3 Step 1: Accept your invite from GitHub
@ -65,19 +65,16 @@ block content
a.btn.btn-primary.btn-huge(href='/' + org.name + '/join/express' + (onboarding ? '?onboarding=' + onboarding : ''))
| Join #{org.name} now
p.
Click to authorize this portal to use an additional GitHub API scope,
<a href="https://developer.github.com/v3/oauth/#scopes" target="_new"><code>org:write</code></a>
to automate the join process. <em>The additional scope will be removed the next time you sign
in to this portal.</em>
Authorize this site to use the
<a href="https://developer.github.com/v3/oauth/#scopes" target="_new"><code>org:write</code></a>
scope to automate the join process.
//-<em>The additional scope will be removed the next time you sign in to this portal.</em>
hr
form(method='post')
p
input.btn.btn-primary.btn-lg(type='submit', value='Join ' + org.name + ' (Manual)')
input.btn.btn-default.btn(type='submit', value='Join ' + org.name + ' (Manual)')
p.
This alternate path does not increase the scope of permissions granted to this portal. After
clicking the manual join button, we will 1) send you an invitation from #{org.name}, 2) ask
you to accept the invitation over on GitHub.com, and then 3) verify that the invitation was
accepted properly.
This alternate path will not increase the scope of permissions granted to this site. Instead, the process requires many manual steps:
ul
li An invitation will be sent from GitHub
li On the next page we'll explain how the GitHub invitation works
@ -87,15 +84,13 @@ block content
hr
h3 Your onboarding progress
h5
| Sign in with GitHub & #{config.companyName} IT
|
| Sign in with GitHub & #{config.companyName}&nbsp;
i.glyphicon.glyphicon-ok
h5
| Link your identity
|
| Link your identity&nbsp;
i.glyphicon.glyphicon-ok
h5.text-primary
| Join and accept your first organization invite from GitHub
| Join your first GitHub organization
h5
| Multifactor security checkup
h5
@ -113,10 +108,10 @@ block content
h4= orgUser.login
p(style='margin-top:18px')
a.btn.btn-sm.btn-muted(href='https://github.com/' + org.name, target='_new')
| View on GitHub
| View on GitHub
i.glyphicon.glyphicon-share-alt
hr
div.row
div.col-md-6.col-lg-6
if orgUser.company
@ -132,16 +127,15 @@ block content
h6 On the Web
p
a(href=orgUser.otherFields.blog, target='_new')
= orgUser.otherFields.blog
|
= orgUser.otherFields.blog + ' '
i.glyphicon.glyphicon-share-alt
if orgUser.getProfileCreatedDate()
h6 Created
p
p
time(datetime=orgUser.getProfileCreatedDate().toISOString())= orgUser.getProfileCreatedDate().toDateString()
if orgUser.getProfileCreatedDate()
h6 Updated
p
p
time(datetime=orgUser.getProfileUpdatedDate().toISOString())= orgUser.getProfileUpdatedDate().toDateString()
hr
if org.inner.settings.organizationPurpose
@ -167,7 +161,7 @@ block content
h2
= orgUser.otherFields.total_private_repos + ' '
small Private
hr
h6 #{config.companyName} Investment
if orgUser.otherFields.plan && orgUser.otherFields.plan.private_repos
@ -179,4 +173,4 @@ block content
small Remaining Private Repos
h2
| &infin;&nbsp;
small Remaining OSS Repos
small Remaining Public Repos

Просмотреть файл

@ -25,26 +25,22 @@ block content
a.btn.btn-primary(href=org.baseUrl + (onboarding ? 'membership?onboarding=' + onboarding : 'membership')) Continue
| &nbsp;&nbsp;
a.btn.btn-default(href='https://github.com/settings/profile', target='_new')
| Edit your profile in a new tab
| Edit your profile in a new tab&nbsp;
i.glyphicon.glyphicon-share-alt
if onboarding
hr
h3 Your onboarding progress
h5
| Sign in with GitHub & #{config.companyName} IT
|
| Sign in with GitHub & #{config.companyName}&nbsp;
i.glyphicon.glyphicon-ok
h5
| Link your identity
|
| Link your identity&nbsp;
i.glyphicon.glyphicon-ok
h5
| Join and accept your first organization invite from GitHub
|
| Join your first GitHub organization&nbsp;
i.glyphicon.glyphicon-ok
h5
| Multifactor security checkup
|
| Multifactor security checkup&nbsp;
i.glyphicon.glyphicon-ok
h5.text-primary
| Profile review
@ -87,10 +83,10 @@ block content
span
p
| Web
br
br
a(href=userProfile.otherFields.blog, target='_new')
strong= userProfile.otherFields.blog
p(style='margin:16px 0')
a.btn.btn-default.btn-sm(href='https://github.com/settings/profile', target='_new')
| Edit your profile in a new tab
| Edit your profile in a new tab&nbsp;
i.glyphicon.glyphicon-share-alt

Просмотреть файл

@ -11,7 +11,7 @@ extends layout
block content
div.container
h1 Remove your corporate association with #{user.github.displayName || user.github.username}?
h1 Unlink #{user.github.username}?
if orgs && orgs.length && orgs.length > 0
p You are currently a member of the following #{config.companyName} organization#{orgs.length == 1 ? '' : 's'} on GitHub:
@ -21,23 +21,21 @@ block content
p Please carefully review this page. Removing your corporate association will remove you from all of the organizations listed above.
p.
<strong>Potential data loss warning:</strong> If you use private repos with your
project, and have changes in a private fork that have not yet been committed to the
<strong>Potential data loss warning:</strong> If you use private repos with your
project, and have changes in a private fork that have not yet been committed to the
upstream repo for the org, they will be lost when membership is removed.
p By continuing, you understand:
p Please be very careful and review the following before deciding whether to "unlink" your GitHub account:
ul
li Your GitHub account #{user.github.username} will lose access to #{config.companyName} organizations
li Private forks of repos from these orgs, if any, will be removed by GitHub
li Work committed in a private fork of a #{config.companyName} org will be lost
li My GitHub account #{user.github.username} will lose access to any of my organizations
li Any private forks of repos from my orgs will be removed by GitHub
li Any work done in a private fork of repos from my orgs will be lost. I understand this data loss potential.
li Your account will no longer be part of the audit process and is again my own responsibility.
p If you have any questions about this, please contact your team's GitHub liasons or your corporate legal contact.
form(method='post', action='/unlink')
p
input.btn.btn-primary(type='submit', value='I Agree. Remove my corporate access.')
| &nbsp;
a.btn.btn-default(href='/') Cancel
input.btn.btn-lg.btn-primary(type='submit', value='Remove my corporate link')
| &nbsp; &nbsp; &nbsp;
a.btn.btn-lg.btn-default(href='/') Cancel

Просмотреть файл

@ -9,20 +9,45 @@ block content
div.container
div.row
div.col-md-6.col-lg-6.col-sm-6
h1 Hi, #{user.github.displayName || user.github.username}
h2 #{config.companyName} employees & vendors
p.lead To onboard, please authenticate with Active Directory.
p
a.btn.btn-lg.btn-primary(href='/signin/azure') Sign in to #{config.companyName} IT
if config.primaryAuthenticationScheme === 'github'
h1 Hi, #{user.github.displayName || user.github.username}
h2 #{config.companyName} employees &amp; vendors
else if config.primaryAuthenticationScheme === 'aad'
h1 Hi, #{user.azure.displayName || user.azure.username}
if config.primaryAuthenticationScheme === 'github'
p.lead To onboard, please authenticate with Active Directory.
p
a.btn.btn-lg.btn-primary(href='/signin/azure') Sign in to #{config.companyName}
else if config.primaryAuthenticationScheme === 'aad'
p.lead Please authenticate with the GitHub account that you want to use for your #{config.companyName} work.
div
a.btn.btn-primary.btn-huge.full-width(href='/signin/github')
h3 I already have a GitHub username
p Sign In
div
a.btn.btn-default.full-width(href='/signin/github/join')
h3 I'm new to GitHub
p Let's create a brand new GitHub username
div.col-md-6.col-lg-6.col-sm-6
//-div.alert-gray(style='padding:12px')
div(style='padding:12px')
//h3 Onboarding
p.lead Joining for the first time should take 2-15 minutes
p The initial sign-up process should take <strong>2-15 minutes</strong>, depending on whether your GitHub account already has multi-factor authentication enabled.
p This onboarding experience will:
p.lead Joining should take 2-15 minutes
p Onboarding goes pretty quickly, depending on whether you already have a GitHub account and GitHub multi-factor authentication setup.
ul
li Validate the corporate identity to associate with #{user.github.username}
li Step you through joining the GitHub organization
if config.primaryAuthenticationScheme === 'github'
li Validate the corporate account to link with #{user.github.username}
if config.primaryAuthenticationScheme === 'aad'
li Validate the GitHub account to link with #{user.azure.username}
li Join a #{config.companyName} GitHub organization
li Verify and/or configure the two-factor security of your GitHub account
li Allow you to request access to any team projects
li Allow you to request access to GitHub teams
if config.primaryAuthenticationScheme === 'aad'
hr
p.lead One or two GitHub usernames?
p.
Since Git repositories can be locally configured to mark commits
with a name and e-mail address, it's easy to use a single GitHub
account for both corporate and other GitHub projects you may
work with.
p
a(href='about:blank', target='_new') Click here for more information