[next] Admin Configure (#2076)

* feat: Add RadioButton and CheckBox

* feat: configure facebook and google auth

* feat: configure sso, localAuth and displayName + some tests

* test: add integration tests for configure auth

* test: more integration tests

* feat: add oidc support

* test: add oidc integration test

* feat: generate sso key initially

* fix: import fetchQuery from correct package

* fix: admin url

* fix: set timezone to utc when testing

* refactor: improve route config

* fix: remove obsolete line

* fix: clientMutationId increment

* fix: oidc only create when enabled

* fix: copy

* test: update snapshots

* feat: fixed graphql logging extension

* Update src/locales/en-US/admin.ftl

Co-Authored-By: cvle <vinh@wikiwi.io>

* Apply suggestions from code review

Co-Authored-By: cvle <vinh@wikiwi.io>

* test: update snapshots

* fix: change Local Auth to Email Authentication

* fix: copy updates
This commit is contained in:
Kiwi
2018-11-19 22:47:32 +00:00
committed by Wyatt Johnson
parent 3f949b3712
commit 05350d651f
215 changed files with 7774 additions and 939 deletions
@@ -195,7 +195,6 @@ exports[`accepts correct password 1`] = `
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
@@ -405,7 +404,6 @@ exports[`accepts valid email 1`] = `
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
@@ -628,7 +626,6 @@ exports[`checks for invalid email 1`] = `
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
@@ -825,7 +822,6 @@ exports[`renders sign in form 1`] = `
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
@@ -1048,7 +1044,6 @@ exports[`shows error when submitting empty form 1`] = `
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
@@ -1245,7 +1240,6 @@ exports[`shows server error 1`] = `
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
@@ -1447,7 +1441,6 @@ exports[`shows server error 2`] = `
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
@@ -1644,7 +1637,6 @@ exports[`submits form successfully 1`] = `
disabled={true}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
@@ -1841,7 +1833,6 @@ exports[`submits form successfully 2`] = `
disabled={false}
onBlur={[Function]}
onFocus={[Function]}
onMouseDown={[Function]}
onMouseOut={[Function]}
onMouseOver={[Function]}
onTouchEnd={[Function]}
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,287 @@
import { cloneDeep, get, merge } from "lodash";
import sinon from "sinon";
import { timeout } from "talk-common/utils";
import {
createSinonStub,
inputPredicate,
limitSnapshotTo,
replaceHistoryLocation,
} from "talk-framework/testHelpers";
import create from "../create";
import { settings } from "../fixtures";
beforeEach(async () => {
replaceHistoryLocation("http://localhost/admin/configure/auth");
});
const createTestRenderer = async (resolver: any = {}) => {
const resolvers = {
...resolver,
Query: {
...resolver.Query,
settings: sinon
.stub()
.returns(merge(settings, get(resolver, "Query.settings"))),
},
};
const { testRenderer } = create({
// Set this to true, to see graphql responses.
logNetwork: false,
resolvers,
initLocalState: localRecord => {
localRecord.setValue(true, "loggedIn");
},
});
await timeout();
return testRenderer;
};
it("renders configure auth", async () => {
const testRenderer = await createTestRenderer();
expect(
limitSnapshotTo("configure-container", testRenderer.toJSON())
).toMatchSnapshot();
});
it("regenerate sso key", async () => {
const testRenderer = await createTestRenderer({
Mutation: {
regenerateSSOKey: createSinonStub(s =>
s.callsFake((_: any, data: any) => {
return {
settings: {
auth: {
integrations: {
sso: {
key: "==GENERATED_KEY==",
keyGeneratedAt: "2018-11-12T23:26:06.239Z",
},
},
},
},
clientMutationId: data.input.clientMutationId,
};
})
),
},
});
testRenderer.root
.find(inputPredicate("auth.integrations.sso.enabled"))
.props.onChange({});
testRenderer.root
.find(inputPredicate("configure-auth-sso-regenerate"))
.props.onClick();
await timeout();
expect(
limitSnapshotTo("configure-auth-sso-key", testRenderer.toJSON())
).toMatchSnapshot();
});
it("change settings", async () => {
let settingsRecord = cloneDeep(settings);
const testRenderer = await createTestRenderer({
Query: {
discoverOIDCConfiguration: createSinonStub(s =>
s.callsFake((_: any, data: any) => {
expect(data).toEqual({ issuer: "http://issuer.com" });
return {
issuer: "http://issuer.com",
tokenURL: "http://issuer.com/tokenURL",
jwksURI: "http://issuer.com/jwksURI",
authorizationURL: "http://issuer.com/authorizationURL",
};
})
),
},
Mutation: {
updateSettings: createSinonStub(s =>
s.callsFake((_: any, data: any) => {
expect(data.input.settings.auth.integrations.facebook).toEqual({
enabled: true,
allowRegistration: true,
targetFilter: {
admin: true,
stream: true,
},
clientID: "myClientID",
clientSecret: "myClientSecret",
});
settingsRecord = merge(settingsRecord, data.input.settings);
return {
settings: settingsRecord,
clientMutationId: data.input.clientMutationId,
};
})
),
createOIDCAuthIntegration: createSinonStub(s =>
s.callsFake((_: any, data: any) => {
expect(data.input.configuration).toEqual({
enabled: true,
allowRegistration: false,
targetFilter: {
admin: true,
stream: true,
},
name: "name",
clientID: "clientID",
clientSecret: "clientSecret",
issuer: "http://issuer.com",
jwksURI: "http://issuer.com/jwksURI",
authorizationURL: "http://issuer.com/authorizationURL",
tokenURL: "http://issuer.com/tokenURL",
});
(settingsRecord.auth.integrations.oidc as any).push({
id: "generatedID",
enabled: false,
callbackURL: "http://localhost/oidc/callback",
...data.input.configuration,
});
return {
settings: settingsRecord,
clientMutationId: data.input.clientMutationId,
};
})
),
updateOIDCAuthIntegration: createSinonStub(s =>
s.callsFake((_: any, data: any) => {
expect(data.input.configuration).toEqual({
enabled: true,
allowRegistration: false,
targetFilter: {
admin: true,
stream: true,
},
name: "name",
clientID: "clientID",
clientSecret: "clientSecret2",
issuer: "http://issuer.com",
jwksURI: "http://issuer.com/jwksURI",
authorizationURL: "http://issuer.com/authorizationURL",
tokenURL: "http://issuer.com/tokenURL",
});
(settingsRecord.auth.integrations.oidc[0] as any) = merge(
settingsRecord.auth.integrations.oidc[0],
data.input.configuration
);
return {
integration: settingsRecord.auth.integrations.oidc[0],
settings: settingsRecord,
clientMutationId: data.input.clientMutationId,
};
})
),
},
});
// Let's change some facebook settings.
testRenderer.root
.find(inputPredicate("auth.integrations.facebook.enabled"))
.props.onChange({});
testRenderer.root
.find(inputPredicate("auth.integrations.facebook.clientID"))
.props.onChange("myClientID");
testRenderer.root
.find(inputPredicate("auth.integrations.facebook.clientSecret"))
.props.onChange("myClientSecret");
expect(
limitSnapshotTo("configure-auth-facebook-container", testRenderer.toJSON())
).toMatchSnapshot("enable facebook configure box");
// Send form, this will perform creating an initial oidc record and update settings.
testRenderer.root.findByProps({ id: "configure-form" }).props.onSubmit();
// Submit button should be disabled.
expect(
testRenderer.root.find(inputPredicate("configure-sideBar-saveChanges"))
.props.disabled
).toBe(true);
// Disable other fields while submitting
// We are only testing for one here right now..
expect(
testRenderer.root.find(inputPredicate("auth.integrations.facebook.enabled"))
.props.disabled
).toBe(true);
await timeout();
expect(
testRenderer.root.find(inputPredicate("auth.integrations.facebook.enabled"))
.props.disabled
).toBe(false);
// Now let's enable oidc
testRenderer.root
.find(inputPredicate("auth.integrations.oidc.0.enabled"))
.props.onChange({});
expect(
limitSnapshotTo("configure-auth-oidc-container-0", testRenderer.toJSON())
).toMatchSnapshot("enable oidc configure box");
// Try to submit form, this will give validation error messages.
testRenderer.root.findByProps({ id: "configure-form" }).props.onSubmit();
expect(
limitSnapshotTo("configure-auth-oidc-container-0", testRenderer.toJSON())
).toMatchSnapshot("oidc validation errors");
// Fill form
testRenderer.root
.find(inputPredicate("auth.integrations.oidc.0.name"))
.props.onChange("name");
testRenderer.root
.find(inputPredicate("auth.integrations.oidc.0.clientID"))
.props.onChange("clientID");
testRenderer.root
.find(inputPredicate("auth.integrations.oidc.0.clientSecret"))
.props.onChange("clientSecret");
testRenderer.root
.find(inputPredicate("auth.integrations.oidc.0.issuer"))
.props.onChange("http://issuer.com");
// Discover the rest.
testRenderer.root
.find(inputPredicate("configure-auth-oidc-discover-0"))
.props.onClick();
await timeout();
// Try to submit again, this should work now.
testRenderer.root.findByProps({ id: "configure-form" }).props.onSubmit();
expect(
limitSnapshotTo("configure-auth-oidc-container-0", testRenderer.toJSON())
).toMatchSnapshot("during submit: oidc without errors");
// Disable other fields while submitting
// We are only testing for one here right now..
expect(
testRenderer.root.find(inputPredicate("auth.integrations.oidc.0.enabled"))
.props.disabled
).toBe(true);
await timeout();
expect(
testRenderer.root.find(inputPredicate("auth.integrations.oidc.0.enabled"))
.props.disabled
).toBe(false);
// Change clientSecret
testRenderer.root
.find(inputPredicate("auth.integrations.oidc.0.clientSecret"))
.props.onChange("clientSecret2");
testRenderer.root.findByProps({ id: "configure-form" }).props.onSubmit();
// Disable other fields while submitting
// We are only testing for one here right now..
expect(
testRenderer.root.find(inputPredicate("auth.integrations.oidc.0.enabled"))
.props.disabled
).toBe(true);
await timeout();
expect(
testRenderer.root.find(inputPredicate("auth.integrations.oidc.0.enabled"))
.props.disabled
).toBe(false);
});
+50
View File
@@ -0,0 +1,50 @@
export const settings = {
auth: {
displayName: {
enabled: false,
},
integrations: {
oidc: [],
local: {
enabled: false,
allowRegistration: true,
targetFilter: {
admin: true,
stream: true,
},
},
sso: {
enabled: false,
allowRegistration: true,
targetFilter: {
admin: true,
stream: true,
},
key: null,
keyGeneratedAt: null,
},
google: {
enabled: false,
allowRegistration: true,
targetFilter: {
admin: true,
stream: true,
},
clientID: "",
clientSecret: "",
callbackURL: "http://localhost/google/callback",
},
facebook: {
enabled: false,
allowRegistration: true,
targetFilter: {
admin: true,
stream: true,
},
clientID: "",
clientSecret: "",
callbackURL: "http://localhost/facebook/callback",
},
},
},
};
@@ -23,9 +23,9 @@ it("redirect to redirectPath when already logged in", async () => {
logNetwork: false,
initLocalState: localRecord => {
localRecord.setValue(true, "loggedIn");
localRecord.setValue("/admin/configure", "redirectPath");
localRecord.setValue("/admin/community", "redirectPath");
},
});
await timeout();
expect(window.location.toString()).toBe("http://localhost/admin/configure");
expect(window.location.toString()).toBe("http://localhost/admin/community");
});