Skip to content

Commit

Permalink
Fix: updates to accounts-ghe (fixes #201 , #200) (#317)
Browse files Browse the repository at this point in the history
* Fix: updates to accounts-ghe (fixes #201 , #200 )

- Make accounts-ghe a local package
- don't send auth token to github as part of params
- don't require GITHUB_API and GITHUB_URL for ghe logins
- sanitize GHE url input on the initial razeedash configuration screen

* add packages/accounts-ghe

* Update packages

* modernize accounts-ghe and make it a local package
  • Loading branch information
dalehille committed Jun 16, 2020
1 parent 206b792 commit 39faece
Show file tree
Hide file tree
Showing 18 changed files with 306 additions and 61 deletions.
2 changes: 1 addition & 1 deletion .meteor/packages
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ meteortesting:mocha-core@5.2.0_3
gadicc:blaze-react-component
accounts-github@1.4.3
github-config-ui@1.0.1
ibmcloud:accounts-ghe
ibmcloud:accounts-ghe@3.0.0
useraccounts:core
useraccounts:unstyled
service-configuration@1.0.11
Expand Down
6 changes: 3 additions & 3 deletions .meteor/versions
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
accounts-base@1.6.0
accounts-github@1.4.3
accounts-oauth@1.2.0
accounts-password@1.6.0
accounts-password@1.6.1
accounts-ui@1.3.1
accounts-ui-unstyled@1.4.2
ahref:flow-router-breadcrumb@1.1.0
Expand Down Expand Up @@ -51,7 +51,7 @@ hot-code-push@1.0.4
html-tools@1.0.11
htmljs@1.0.11
http@1.4.2
ibmcloud:accounts-ghe@2.0.5
ibmcloud:accounts-ghe@3.0.0
id-map@1.1.0
inter-process-messaging@0.1.1
johanbrook:publication-collector@1.1.0
Expand All @@ -69,7 +69,7 @@ meteor-base@1.4.0
meteortesting:browser-tests@1.0.0
meteortesting:mocha@1.1.3
meteortesting:mocha-core@5.2.0_3
minifier-css@1.5.0
minifier-css@1.5.1
minifier-js@2.6.0
minimongo@1.6.0
mobile-experience@1.1.0
Expand Down
14 changes: 0 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,6 @@ When deploying RazeeDash these variables can be set by adding them to the `razee
| MONGO_URL | Required | - | URL to your mongo instance |
| MONGO_OPTIONS | Optional | - | Set additional mongo connection string options |
| OAUTH_SECRET_KEY | Optional | - | GitHub OAuth Secret Key |
| GITHUB_URL | Optional | <https://github.com/> | Required when using GitHub Enterprise authentication |
| GITHUB_API | Optional | <https://api.github.com/> | Required when using GitHub Enterprise authentication |
| BITBUCKET_URL | Optional | <https://bitbucket.org/> | Required when using Bitbucket Enterprise authentication |
| BITBUCKET_API | Optional | <https://api.bitbucket.org/2.0/> | Required when using Bitbucket Enterprise authentication |
| BUILD_ID | Optional | Travis build ID | Travis Build ID |
Expand All @@ -52,18 +50,6 @@ Example registration for running locally.
| Homepage URL | <http://localhost:3000> |
| User authorization callback URL | <http://localhost:3000/_oauth/github> |

### Using GitHub Enterprise for authentication

- Add `github_url` and `github_api` to the `razeedash-config` ConfigMap. For example:

```yaml
apiVersion: v1
kind: ConfigMap
data:
github_url: https://github.your_company.com/
github_api: https://github.your_company.com/api/v3/
```

### Using Bitbucket Enterprise authentication

Add `bitbucket_url` and `bitbucket_api` to the `razeedash-config` ConfigMap. For example:
Expand Down
13 changes: 10 additions & 3 deletions imports/api/lib/ghe.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,13 +19,20 @@ import { OAuth } from 'meteor/oauth';
import { HTTP } from 'meteor/http';
import log from './log.js';
import _ from 'lodash';
import { loginType } from './login.js';
import { ServiceConfiguration } from 'meteor/service-configuration';
import { loginType, sanitizeUrl } from './login.js';

function listOrgs(loggedInUserObj){
const url = `${Meteor.settings.public.GITHUB_API}user/memberships/orgs?state=active&per_page=100`;
const serviceName = loginType();
let gitUrl = 'https://api.github.com';
if(serviceName === 'ghe') {
let {gheURL} = ServiceConfiguration.configurations.findOne({ service: 'ghe' }, {fields: {gheURL: 1}});
gitUrl = sanitizeUrl(gheURL) + '/api/v3';
}

const url = `${gitUrl}/user/memberships/orgs?state=active&per_page=100`;
const token = OAuth.openSecret( loggedInUserObj.services[serviceName].accessToken, loggedInUserObj._id );

const options = {
headers: {
Authorization: `bearer ${token}`,
Expand Down
15 changes: 14 additions & 1 deletion imports/api/lib/login.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,19 @@ AccountsTemplates.configure({
},
});

const sanitizeUrl = (url) => {

const httpCheck = /^((http|https):\/\/)/;
if(!httpCheck.test(url)) {
url = `https://${url}`;
}

const trailingSlash = /\/*$/gi;
const newUrl = url.replace(trailingSlash, '');

return newUrl;
};

// A user can logon via github, github enterprise, bitbucket or they can create a local id/password stored in mongo
// `localUser` is used throughout our code so that we can skip calls to the github api for local users
function localUser() {
Expand All @@ -47,4 +60,4 @@ function getServiceConfiguration() {
return config ? config.service : undefined;
}

export { localUser, loginType, getServiceConfiguration };
export { localUser, loginType, getServiceConfiguration, sanitizeUrl };
20 changes: 6 additions & 14 deletions imports/startup/client/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,8 @@ import { Orgs } from '/imports/api/org/orgs';
import { Clusters } from '/imports/api/cluster/clusters/clusters';
import { Session } from 'meteor/session';
import { Accounts } from 'meteor/accounts-base';
import { localUser, loginType, getServiceConfiguration } from '/imports/api/lib/login.js';
import { localUser, loginType, getServiceConfiguration, sanitizeUrl } from '/imports/api/lib/login.js';
import { ServiceConfiguration } from 'meteor/service-configuration';

Accounts.ui.config( { requestPermissions: {
github: ['read:user', 'read:org'],
Expand Down Expand Up @@ -145,19 +146,16 @@ Template.registerHelper('meteorSetting', (name)=>{
return _.get(Meteor.settings.public, name, null);
});

Template.registerHelper('githubUrl', ()=>{
return Meteor.settings.public.GITHUB_URL;
});
Template.registerHelper('bitbucketUrl', ()=>{
return Meteor.settings.public.BITBUCKET_URL;
});
Template.registerHelper('scmUrl', ()=>{
const service = getServiceConfiguration();
let scmLink = '';
if(service === 'bitbucket') {
scmLink = Meteor.settings.public.BITBUCKET_URL;
} else if(service === 'ghe') {
let {gheURL} = ServiceConfiguration.configurations.findOne({ service: 'ghe' }, {fields: {gheURL: 1}});
scmLink = sanitizeUrl(gheURL);
} else {
scmLink = Meteor.settings.public.GITHUB_URL;
scmLink = 'https://github.com/';
}
return scmLink.endsWith('/') ? scmLink : scmLink + '/';
});
Expand Down Expand Up @@ -292,12 +290,6 @@ Template.registerHelper('outputDisabledFlipped', (disabled) => {
return (disabled ? '' : 'disabled');
});

Template.registerHelper('commitHref', (commitId, deployment=null)=>{
const githubOrgName = Session.get('currentOrgName');
const githubProjName = deployment.searchableData.name;
return `${Meteor.settings.public.GITHUB_URL}${githubOrgName}/${githubProjName}/commit/${commitId}`;
});

Template.registerHelper('valuesJoined', (values) => {
if (values.length == 1) {
return `"${values[0]}"`;
Expand Down
4 changes: 1 addition & 3 deletions imports/startup/server/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -80,8 +80,6 @@ const migrateUserOrgs = () => {

Meteor.startup(()=>{
// envs copied over to client
Meteor.settings.public.GITHUB_URL = process.env.GITHUB_URL || 'https://github.com/';
Meteor.settings.public.GITHUB_API = process.env.GITHUB_API || 'https://api.github.com/';
Meteor.settings.public.BITBUCKET_URL = process.env.BITBUCKET_URL || 'https://bitbucket.org/';
Meteor.settings.public.BITBUCKET_API = process.env.BITBUCKET_API || 'https://api.bitbucket.org/2.0/';
Meteor.settings.public.RAZEE_GITHUB_URL = 'https://github.com/razee-io/razeedash';
Expand All @@ -93,7 +91,7 @@ Meteor.startup(()=>{
};
versionInfo.str = `${versionInfo.buildId}_${versionInfo.lastCommitId}`;
Meteor.settings.public.version = versionInfo;

if ( process.env.OAUTH_SECRET_KEY ) {
migrateUnencryptedUsers();
}
Expand Down
3 changes: 3 additions & 0 deletions imports/ui/pages/razeeWelcome/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,9 @@ Template.SelectOrg.events({
Template.SelectOrg_git.helpers({
scmIcon() {
return loginType() === 'bitbucket' ? 'fa-bitbucket' : 'fa-github';
},
orgName() {
return Template.currentData().org.name;
}
});

Expand Down
2 changes: 1 addition & 1 deletion imports/ui/pages/razeeWelcome/page.html
Original file line number Diff line number Diff line change
Expand Up @@ -187,5 +187,5 @@ <h5 class="modal-title">Remove "{{orgName}}"</h5>


<template name="SelectOrg_git">
<a href="{{scmUrl}}{{org.name}}" class="ml-2 btn btn-sm btn-light fa {{scmIcon}} rounded-0 git-img action-button" alt="{{org.name}} link"></a>
<a href="{{scmUrl}}{{orgName}}" class="ml-2 btn btn-sm btn-light fa {{scmIcon}} rounded-0 git-img action-button" alt="{{orgName}} link"></a>
</template>
12 changes: 0 additions & 12 deletions kubernetes/razeedash/resource.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -55,18 +55,6 @@ items:
name: razeedash-secret
key: oauth_secret_key
optional: true
- name: GITHUB_URL
valueFrom:
configMapKeyRef:
name: razeedash-config
key: github_url
optional: true
- name: GITHUB_API
valueFrom:
configMapKeyRef:
name: razeedash-config
key: github_api
optional: true
- name: BITBUCKET_URL
valueFrom:
configMapKeyRef:
Expand Down
18 changes: 9 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

21 changes: 21 additions & 0 deletions packages/ghe/ghe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* eslint-disable no-undef */

Accounts.oauth.registerService('ghe');

if (Meteor.isClient) {
Meteor.loginWithGhe = function(options, callback) {
// support a callback without options
if (!callback && typeof options === 'function') {
callback = options;
options = null;
}

const credentialRequestCompleteCallback = Accounts.oauth.credentialRequestCompleteHandler(callback);
Ghe.requestCredential(options, credentialRequestCompleteCallback);
};
} else {
Accounts.addAutopublishFields({
forLoggedInUser: ['services.ghe'],
forOtherUsers: ['services.ghe.username']
});
}
57 changes: 57 additions & 0 deletions packages/ghe/ghe_client.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
/* eslint-disable no-undef */

Ghe = {};

// Request Github credentials for the user
// @param options {optional}
// @param credentialRequestCompleteCallback {Function} Callback function to call on
// completion. Takes one argument, credentialToken on success, or Error on
// error.
Ghe.requestCredential = function(options, credentialRequestCompleteCallback) {
// support both (options, callback) and (callback).
if (!credentialRequestCompleteCallback && typeof options === 'function') {
credentialRequestCompleteCallback = options;
options = {};
}

const config = ServiceConfiguration.configurations.findOne({ service: 'ghe' });
if (!config) {
credentialRequestCompleteCallback && credentialRequestCompleteCallback(new ServiceConfiguration.ConfigError());
return;
}
const credentialToken = Random.secret();

const scope = (options && options.requestPermissions) || ['user:email'];
const flatScope = _.map(scope, encodeURIComponent).join('+');

const loginStyle = OAuth._loginStyle('ghe', config, options);

let url = config.gheURL;
const httpCheck = /^((http|https):\/\/)/;
if(!httpCheck.test(config.gheURL)) {
url = `https://${config.gheURL}`;
}

const trailingSlash = /\/*$/gi;
const gheUrl = url.replace(trailingSlash, '');

const loginUrl =
gheUrl + '/login/oauth/authorize' +
'?client_id=' + config.clientId +
'&scope=' + flatScope +
'&redirect_uri=' + OAuth._redirectUri('ghe', config) +
'&state=' + OAuth._stateParam(loginStyle, credentialToken, options && options.redirectUrl);

OAuth.launchLogin({
loginService: 'ghe',
loginStyle,
loginUrl,
credentialRequestCompleteCallback,
credentialToken,
popupOptions: {
width: 900,
height: 450
}
});

};
16 changes: 16 additions & 0 deletions packages/ghe/ghe_configure.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<template name="configureLoginServiceDialogForGhe">
<p>
First, you'll need to get a GitHub Client ID. Follow these steps:
</p>
<ol>
<li>
Visit your enterprise GitHub site and go to<br/> Settings -> Developer settings -> OAuth Apps -> New OAuth App
</li>
<li>
Set Homepage URL to: <span class="url">{{siteUrl}}</span>
</li>
<li>
Set Authorization callback URL to: <span class="url">{{siteUrl}}_oauth/ghe</span>
</li>
</ol>
</template>
13 changes: 13 additions & 0 deletions packages/ghe/ghe_configure.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
/* eslint-disable no-undef */

Template.configureLoginServiceDialogForGhe.helpers({
siteUrl: function() {
return Meteor.absoluteUrl();
}
});

Template.configureLoginServiceDialogForGhe.fields = () => [
{ property: 'gheURL', label: 'GitHub Enterprise URL' },
{ property: 'clientId', label: 'Client ID' },
{ property: 'secret', label: 'Client Secret' }
];
3 changes: 3 additions & 0 deletions packages/ghe/ghe_login_button.css

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading

0 comments on commit 39faece

Please sign in to comment.