Skip to content
Open
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
5 changes: 4 additions & 1 deletion src/components/clock.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
import React from 'react';
import moment from "moment-timezone";
import FragmentParser from "./fragment-parser";
import {getTimeServiceUrl} from '../utils/methods';
import {getTimeServiceUrl} from '../utils/config';

/**
* class Clock
Expand Down Expand Up @@ -113,6 +113,9 @@ class Clock extends React.Component {

getServerTime = () => {
const timeServiceUrl = getTimeServiceUrl();
// No endpoint configured: go straight to the local-clock fallback
// instead of fetching '' or "undefined".
if (!timeServiceUrl) return Promise.reject(null);
return fetch(`${timeServiceUrl}`).then(async (response) => {
if (response.status === 200) {
return response.json();
Expand Down
3 changes: 2 additions & 1 deletion src/components/exclusive-wrapper.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@
**/

import React from 'react'
import { getExclusiveSections } from '../utils/config';


export default class Exclusive extends React.Component {
Expand All @@ -24,7 +25,7 @@ export default class Exclusive extends React.Component {

showField() {
let {name} = this.props;
let exclusiveSections = window.EXCLUSIVE_SECTIONS;
let exclusiveSections = getExclusiveSections();

return exclusiveSections ? exclusiveSections.includes(name) : false;
}
Expand Down
3 changes: 2 additions & 1 deletion src/components/security/__tests__/get-user-info.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@ jest.mock("../../../utils/actions", () => ({
startLoading: jest.fn(() => ({ type: "START_LOADING" })),
stopLoading: jest.fn(() => ({ type: "STOP_LOADING" })),
}));
jest.mock("../../../utils/methods", () => ({
jest.mock("../../../utils/config", () => ({
...jest.requireActual("../../../utils/config"),
buildAPIBaseUrl: jest.fn((p) => `BASE${p}`),
getAllowedUserGroups: jest.fn(() => ""),
}));
Expand Down
2 changes: 1 addition & 1 deletion src/components/security/abstract-auth-callback-route-v2.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ import URI from "urijs";
import {
doLogin,
emitAccessToken,
getOAuth2Flow,
RESPONSE_TYPE_IMPLICIT,
RESPONSE_TYPE_CODE,
validateIdToken, getAuthInfo
} from "./methods";
import {getCurrentPathName, getCurrentHref} from '../../utils/methods';
import {getOAuth2Flow} from '../../utils/config';

const AbstractAuthorizationCallbackRouteV2 = ({issuer, audience, location, callback, redirectToError, onUserAuth}) => {
// we only use this for the redirectToError, so its initial state should be true
Expand Down
2 changes: 1 addition & 1 deletion src/components/security/abstract-auth-callback-route.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,12 @@ import URI from "urijs";
import {
doLogin,
emitAccessToken,
getOAuth2Flow,
RESPONSE_TYPE_IMPLICIT,
RESPONSE_TYPE_CODE,
validateIdToken
} from "./methods";
import {getCurrentPathName, getCurrentHref} from '../../utils/methods';
import {getOAuth2Flow} from '../../utils/config';

class AbstractAuthorizationCallbackRoute extends React.Component {

Expand Down
5 changes: 2 additions & 3 deletions src/components/security/actions.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@

import T from "i18n-react/dist/i18n-react";
import {authErrorHandler, createAction, getRequest, showMessage, startLoading, stopLoading} from "../../utils/actions";
import {buildAPIBaseUrl, getAllowedUserGroups} from '../../utils/methods';
import {buildAPIBaseUrl, getAllowedUserGroups, parseUserGroups} from '../../utils/config';
import { getAccessToken, storeAuthInfo, initLogOut} from './methods';

/**
Expand Down Expand Up @@ -50,8 +50,7 @@ export const doLogout = (backUrl) => (dispatch, getState) => {
export const getUserInfo = (expand = 'groups', fields='', backUrl = null, history = null, errorHandler = null ) =>
async (dispatch, getState) => {

let AllowedUserGroups = getAllowedUserGroups();
AllowedUserGroups = AllowedUserGroups !== '' ? AllowedUserGroups.split(' ') : [];
let AllowedUserGroups = parseUserGroups(getAllowedUserGroups());
let {loggedUserState} = getState();
let {member} = loggedUserState;

Expand Down
46 changes: 9 additions & 37 deletions src/components/security/methods.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,13 @@ import URI from "urijs";
import IdTokenVerifier from "idtoken-verifier";
import {SET_LOGGED_USER} from "./actions";
import {getRandomBytes, getSHA256} from "../../utils/crypto";
import {
getOAuth2ClientId,
getOAuth2Flow,
useOAuth2RefreshToken,
getOAuth2IDPBaseUrl,
getOAuth2Scopes,
} from "../../utils/config";

import {
AUTH_ERROR_ACCESS_TOKEN_EXPIRED,
Expand All @@ -32,6 +39,8 @@ import {
AUTH_ERROR_MISSING_NONCE_PARAM,
} from "./constants";

export { getOAuth2ClientId, getOAuth2Flow, useOAuth2RefreshToken, getOAuth2IDPBaseUrl, getOAuth2Scopes } from "../../utils/config";

/**
* @ignore
*/
Expand All @@ -51,7 +60,6 @@ const PKCE = 'pkce';
const ID_TOKEN = 'idToken';
const BACK_ULR_PARAM_NAME = 'BackUrl';


/**
*
* @param backUrl
Expand Down Expand Up @@ -448,7 +456,6 @@ export const clearAccessToken = async () => {
}
}


export const refreshAccessToken = async (refresh_token) => {

let baseUrl = getOAuth2IDPBaseUrl();
Expand Down Expand Up @@ -569,41 +576,6 @@ export const getIdToken = () => {
return null;
};

export const getOAuth2ClientId = () => {
if (typeof window !== 'undefined') {
return window.OAUTH2_CLIENT_ID;
}
return null;
};

export const getOAuth2Flow = () => {
if (typeof window !== 'undefined') {
return window.OAUTH2_FLOW || "token id_token";
}
return "token id_token";
}

export const useOAuth2RefreshToken = () => {
if (typeof window !== 'undefined') {
return new Boolean(window.OAUTH2_USE_REFRESH_TOKEN || true);
}
return true;
}

export const getOAuth2IDPBaseUrl = () => {
if (typeof window !== 'undefined') {
return window.IDP_BASE_URL;
}
return null;
};

export const getOAuth2Scopes = () => {
if (typeof window !== 'undefined') {
return window.SCOPES;
}
return null;
};

export const initLogOut = () => {
let location = getCurrentLocation();
location.replace(getLogoutUrl(getIdToken()).toString());
Expand Down
3 changes: 2 additions & 1 deletion src/components/security/reducers.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ import {

import IdTokenVerifier from 'idtoken-verifier';

import {clearAuthInfo, getIdToken, getOAuth2ClientId, getOAuth2IDPBaseUrl} from './methods';
import {clearAuthInfo, getIdToken} from './methods';
import {getOAuth2ClientId, getOAuth2IDPBaseUrl} from '../../utils/config';

const DEFAULT_STATE = {
isLoggedUser: false,
Expand Down
54 changes: 54 additions & 0 deletions src/utils/__tests__/config-no-window.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
/**
* @jest-environment node
*
* With no window (server rendering) every getter returns its documented
* default, and a configured value is still honored.
*/
import {
setConfig,
buildAPIBaseUrl,
getTimeServiceUrl,
getAllowedUserGroups,
getOAuth2ClientId,
getOAuth2Flow,
useOAuth2RefreshToken,
getOAuth2IDPBaseUrl,
getOAuth2Scopes,
getExclusiveSections,
} from "../config";

describe("config getters without a window", () => {
// The jest config plants a `window` global even in the node environment;
// the getters check `typeof window` at call time, so remove it here.
let savedWindow;
beforeAll(() => {
savedWindow = global.window;
delete global.window;
});
afterAll(() => {
global.window = savedWindow;
});
afterEach(() => setConfig({}));

test("there is no window in this environment", () => {
expect(typeof window).toBe("undefined");
});

test("each getter returns its no-window default", () => {
expect(buildAPIBaseUrl("/x")).toBe(null);
expect(getTimeServiceUrl()).toBe(null);
expect(getAllowedUserGroups()).toBe(null);
expect(getOAuth2ClientId()).toBe(null);
expect(getOAuth2Flow()).toBe("token id_token");
expect(useOAuth2RefreshToken()).toBe(true);
expect(getOAuth2IDPBaseUrl()).toBe(null);
expect(getOAuth2Scopes()).toBe(null);
expect(getExclusiveSections()).toBe(undefined);
});

test("a configured value is returned without a window", () => {
setConfig({ apiBaseUrl: "https://cfg.test", oauth2Flow: "code" });
expect(buildAPIBaseUrl("/x")).toBe("https://cfg.test/x");
expect(getOAuth2Flow()).toBe("code");
});
});
109 changes: 109 additions & 0 deletions src/utils/__tests__/config-reads.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,109 @@
/**
* Every app-level setting uicore needs is read from a window global. These
* tests pin which global each getter reads and what it returns when the
* global is unset.
*/
import React from "react";
import Enzyme, { mount } from "enzyme";
import Adapter from "enzyme-adapter-react-16";
import {
buildAPIBaseUrl,
getTimeServiceUrl,
getAllowedUserGroups,
getOAuth2ClientId,
getOAuth2Flow,
useOAuth2RefreshToken,
getOAuth2IDPBaseUrl,
getOAuth2Scopes,
} from "../config";
import * as utilsMethods from "../methods";
import * as securityMethods from "../../components/security/methods";
import Exclusive from "../../components/exclusive-wrapper";

Enzyme.configure({ adapter: new Adapter() });

const GLOBALS = [
"API_BASE_URL", "TIMEINTERVALSINCE1970_API_URL", "ALLOWED_USER_GROUPS",
"OAUTH2_CLIENT_ID", "OAUTH2_FLOW", "OAUTH2_USE_REFRESH_TOKEN", "IDP_BASE_URL",
"SCOPES", "EXCLUSIVE_SECTIONS",
];

describe("config reads from window globals", () => {
let origEnvTime;

beforeEach(() => {
origEnvTime = process.env.TIMEINTERVALSINCE1970_API_URL;
delete process.env.TIMEINTERVALSINCE1970_API_URL;
GLOBALS.forEach((k) => delete window[k]);
});

afterEach(() => {
if (origEnvTime === undefined) delete process.env.TIMEINTERVALSINCE1970_API_URL;
else process.env.TIMEINTERVALSINCE1970_API_URL = origEnvTime;
GLOBALS.forEach((k) => delete window[k]);
});

test("buildAPIBaseUrl prepends window.API_BASE_URL", () => {
window.API_BASE_URL = "https://api.test";
expect(buildAPIBaseUrl("/api/v1/summits")).toBe("https://api.test/api/v1/summits");
});

test("getTimeServiceUrl reads window.TIMEINTERVALSINCE1970_API_URL, else the env var", () => {
window.TIMEINTERVALSINCE1970_API_URL = "https://time.test";
expect(getTimeServiceUrl()).toBe("https://time.test");
delete window.TIMEINTERVALSINCE1970_API_URL;
process.env.TIMEINTERVALSINCE1970_API_URL = "https://time.env";
expect(getTimeServiceUrl()).toBe("https://time.env");
});

test("getAllowedUserGroups reads window.ALLOWED_USER_GROUPS, else ''", () => {
expect(getAllowedUserGroups()).toBe("");
window.ALLOWED_USER_GROUPS = "admins";
expect(getAllowedUserGroups()).toBe("admins");
});

test("getOAuth2ClientId reads window.OAUTH2_CLIENT_ID", () => {
window.OAUTH2_CLIENT_ID = "cid";
expect(getOAuth2ClientId()).toBe("cid");
});

test("getOAuth2Flow reads window.OAUTH2_FLOW, else 'token id_token'", () => {
expect(getOAuth2Flow()).toBe("token id_token");
window.OAUTH2_FLOW = "code";
expect(getOAuth2Flow()).toBe("code");
});

test("useOAuth2RefreshToken is truthy from the window global, even when it is false", () => {
expect(useOAuth2RefreshToken()).toBeTruthy();
window.OAUTH2_USE_REFRESH_TOKEN = false;
expect(useOAuth2RefreshToken()).toBeTruthy();
});

test("getOAuth2IDPBaseUrl reads window.IDP_BASE_URL", () => {
window.IDP_BASE_URL = "https://idp.test";
expect(getOAuth2IDPBaseUrl()).toBe("https://idp.test");
});

test("getOAuth2Scopes reads window.SCOPES", () => {
window.SCOPES = "openid profile";
expect(getOAuth2Scopes()).toBe("openid profile");
});

test("Exclusive renders its children only when window.EXCLUSIVE_SECTIONS lists the name", () => {
const tree = () => mount(<Exclusive name="sponsors"><span>x</span></Exclusive>);
expect(tree().find("span")).toHaveLength(0);
window.EXCLUSIVE_SECTIONS = ["sponsors"];
expect(tree().find("span")).toHaveLength(1);
});

test("the getters stay exported from utils/methods and security/methods", () => {
expect(utilsMethods.buildAPIBaseUrl).toBe(buildAPIBaseUrl);
expect(utilsMethods.getTimeServiceUrl).toBe(getTimeServiceUrl);
expect(utilsMethods.getAllowedUserGroups).toBe(getAllowedUserGroups);
expect(securityMethods.getOAuth2ClientId).toBe(getOAuth2ClientId);
expect(securityMethods.getOAuth2Flow).toBe(getOAuth2Flow);
expect(securityMethods.useOAuth2RefreshToken).toBe(useOAuth2RefreshToken);
expect(securityMethods.getOAuth2IDPBaseUrl).toBe(getOAuth2IDPBaseUrl);
expect(securityMethods.getOAuth2Scopes).toBe(getOAuth2Scopes);
});
});
Loading
Loading