Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions src/common/__tests__/env.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/* global globalThis */
import test from 'node:test'
import assert from 'node:assert/strict'

import { getEnv, toAppOrigin } from '../env.js'

const envAt = origin => {
globalThis.window = {location: {origin}}
return [getEnv(), getEnv(true)]
}

test('app.openconceptlab.org is production', () => {
assert.deepEqual(envAt('https://app.openconceptlab.org'), ['production', ''])
})

test('app.<env>.openconceptlab.org is that environment', () => {
assert.deepEqual(envAt('https://app.staging.openconceptlab.org'), ['staging', 'staging'])
assert.deepEqual(envAt('https://app.qa.openconceptlab.org'), ['qa', 'qa'])
assert.deepEqual(envAt('https://app.demo.openconceptlab.org'), ['demo', 'demo'])
assert.deepEqual(envAt('https://app.dev.openconceptlab.org'), ['dev', 'dev'])
assert.deepEqual(envAt('https://app.staging.who.openconceptlab.org'), ['staging-who', 'staging.who'])
})

test('app.v2.openconceptlab.org, where TBv2 stays once TBv3 is the default Term Browser, is production', () => {
assert.deepEqual(envAt('https://app.v2.openconceptlab.org'), ['production', ''])
})

test('app.v2.<env>.openconceptlab.org is that environment', () => {
assert.deepEqual(envAt('https://app.v2.staging.openconceptlab.org'), ['staging', 'staging'])
assert.deepEqual(envAt('https://app.v2.qa.openconceptlab.org'), ['qa', 'qa'])
assert.deepEqual(envAt('https://app.v2.demo.openconceptlab.org'), ['demo', 'demo'])
assert.deepEqual(envAt('https://app.v2.dev.openconceptlab.org'), ['dev', 'dev'])
})

test('any other host is development', () => {
assert.deepEqual(envAt('http://localhost:4000'), ['development', 'development'])
})

test('toAppOrigin maps app.v2.<env> to app.<env> and leaves other origins alone', () => {
assert.equal(toAppOrigin('https://app.v2.openconceptlab.org'), 'https://app.openconceptlab.org')
assert.equal(toAppOrigin('https://app.v2.staging.openconceptlab.org'), 'https://app.staging.openconceptlab.org')
assert.equal(toAppOrigin('https://app.qa.openconceptlab.org'), 'https://app.qa.openconceptlab.org')
assert.equal(toAppOrigin('http://localhost:4000'), 'http://localhost:4000')
})
22 changes: 22 additions & 0 deletions src/common/env.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
// TBv2 is served from app[.<env>].openconceptlab.org and from app.v2[.<env>].openconceptlab.org, where it stays
// once TBv3 becomes the default Term Browser (OpenConceptLab/ocl_online#236); either host names the same environment.
export const toAppOrigin = origin => origin.replace('://app.v2.', '://app.')

export const getEnv = forURL => {
const fqdn = toAppOrigin(window.location.origin);

if(fqdn.match('app.staging.openconceptlab'))
return 'staging';
if(fqdn.match('app.qa.openconceptlab'))
return 'qa';
if(fqdn.match('app.demo.openconceptlab'))
return 'demo';
if(fqdn.match('app.dev.openconceptlab'))
return 'dev';
if(fqdn.match('app.staging.who.openconceptlab'))
return forURL ? 'staging.who' : 'staging-who';
if(fqdn.match('app.openconceptlab'))
return forURL ? '' : 'production';

return 'development';
}
34 changes: 10 additions & 24 deletions src/common/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import GAService from '../services/GAService';
import { ensureIdToken } from './ssoSession';
import { SERVER_CONFIGS } from './serverConfigs';
import { windowConfig } from './windowConfig';
import { getEnv, toAppOrigin } from './env';

export const currentPath = () => window.location.hash.split('?')[0];

Expand Down Expand Up @@ -688,24 +689,7 @@ export const arrayToSentence = (arr, separator, lastSeparator=' and ') => {

export const generateRandomString = () => Math.random().toString(36).substring(7);

export const getEnv = forURL => {
const fqdn = window.location.origin;

if(fqdn.match('app.staging.openconceptlab'))
return 'staging';
if(fqdn.match('app.qa.openconceptlab'))
return 'qa';
if(fqdn.match('app.demo.openconceptlab'))
return 'demo';
if(fqdn.match('app.dev.openconceptlab'))
return 'dev';
if(fqdn.match('app.staging.who.openconceptlab'))
return forURL ? 'staging.who' : 'staging-who';
if(fqdn.match('app.openconceptlab'))
return forURL ? '' : 'production';

return 'development';
}
export { getEnv };

export const getOpenMRSURL = () => {
let env = getEnv(true);
Expand Down Expand Up @@ -988,8 +972,10 @@ export const consumeAndValidateOAuthState = returnedState => {
return !returnedState || returnedState === storedState
}

// Sign-in, sign-up and sign-out come back to the host they started on (app.<env> or app.v2.<env>), since the
// PKCE verifier and OAuth state are in that origin's sessionStorage. LOGIN_REDIRECT_URL only turns SSO on.
export const getLoginURL = async returnTo => {
let redirectURL = windowConfig('LOGIN_REDIRECT_URL') || process.env.LOGIN_REDIRECT_URL
let redirectURL = window.location.origin
const oidClientID = windowConfig('OIDC_RP_CLIENT_ID') || process.env.OIDC_RP_CLIENT_ID

redirectURL = redirectURL.replace(/([^:]\/)\/+/g, "$1");
Expand All @@ -1014,7 +1000,7 @@ export const getLoginURL = async returnTo => {
}

export const getRegisterURL = async returnTo => {
let redirectURL = returnTo || windowConfig('LOGIN_REDIRECT_URL') || process.env.LOGIN_REDIRECT_URL
let redirectURL = returnTo || window.location.origin
const oidClientID = windowConfig('OIDC_RP_CLIENT_ID') || process.env.OIDC_RP_CLIENT_ID

redirectURL = redirectURL.replace(/([^:]\/)\/+/g, "$1");
Expand All @@ -1036,9 +1022,9 @@ export const getRegisterURL = async returnTo => {
}

export const getSSOLogoutURL = returnTo => {
const redirectURL = returnTo || windowConfig('LOGIN_REDIRECT_URL') || process.env.LOGIN_REDIRECT_URL
const redirectURL = returnTo || window.location.origin
const idToken = localStorage.id_token
if(redirectURL && idToken)
if(isSSOEnabled() && idToken)
return `${getAPIURL()}/users/logout/?id_token_hint=${idToken}&post_logout_redirect_uri=${redirectURL}`
}

Expand Down Expand Up @@ -1193,7 +1179,7 @@ export const toMapperURL = path => {
if(['app.qa.openconceptlab.org', 'app.demo.openconceptlab.org'].includes(window.location.host))
url = 'https://map.qa.openconceptlab.org'
if(window.location.host.match('app.*.openconceptlab.org'))
url = window.location.origin.replace('//app.', '//map.')
url = toAppOrigin(window.location.origin).replace('//app.', '//map.')

// The Mapper reads auth only from the segment after the first '?' in the referrer's hash, so drop this page's own query
let referrerParams = `referrer=${window.location.href.split('?')[0]}`
Expand Down Expand Up @@ -1271,7 +1257,7 @@ export const toV3URL = path => {
if(window.location.host?.includes('localhost'))
url = 'http://localhost:4002'
if(window.location.host.match('app.*.openconceptlab.org'))
url = window.location.origin.replace('//app.', '//app.v3.')
url = toAppOrigin(window.location.origin).replace('//app.', '//app.v3.')

let referrerParams = `referrer=${window.location.href}`
if(isLoggedIn())
Expand Down
2 changes: 1 addition & 1 deletion src/components/users/OIDLoginCallback.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class OIDLoginCallback extends React.Component {
return
}
this.setState({next: next && next !== '/' ? next : null }, () => {
const redirectURL = this.state.next ? window.location.origin + this.state.next : (windowConfig('LOGIN_REDIRECT_URL') || process.env.LOGIN_REDIRECT_URL)
const redirectURL = this.state.next ? window.location.origin + this.state.next : window.location.origin
const clientId = windowConfig('OIDC_RP_CLIENT_ID') || process.env.OIDC_RP_CLIENT_ID

APIService.users().appendToUrl('oidc/code-exchange/').post({code: code, redirect_uri: redirectURL, client_id: clientId, code_verifier: codeVerifier}).then(res => {
Expand Down
Loading