diff --git a/client/src/components/User/CloudAuth/CloudAuth.test.js b/client/src/components/User/CloudAuth/CloudAuth.test.js deleted file mode 100644 index 705a304b5dd..00000000000 --- a/client/src/components/User/CloudAuth/CloudAuth.test.js +++ /dev/null @@ -1,72 +0,0 @@ -import { shallowMount } from "@vue/test-utils"; -import flushPromises from "flush-promises"; -import { getLocalVue } from "tests/jest/helpers"; - -import { default as CloudAuth } from "./CloudAuth"; -import CloudAuthItem from "./CloudAuthItem"; - -jest.mock("./model/service", () => ({ - listCredentials: async () => { - const listCredentials = require("./testdata/listCredentials.json"); - const Credential = require("./model").Credential; - return listCredentials.map(Credential.create); - }, -})); - -const localVue = getLocalVue(); - -describe("CloudAuth component", () => { - let wrapper; - - beforeEach(async () => { - wrapper = shallowMount(CloudAuth, { localVue }); - await flushPromises(); - }); - - describe("initialization", () => { - it("should render the initial list", () => { - expect(wrapper).toBeTruthy(); - expect(wrapper.findComponent(CloudAuthItem).exists()).toBeTruthy(); - expect(wrapper.vm.items.length == 2).toBeTruthy(); - expect(wrapper.vm.filteredItems.length == 2).toBeTruthy(); - }); - }); - - describe("text filter", () => { - it("should show filter result by text match", () => { - let results; - - wrapper.vm.filter = "aws"; - results = wrapper.vm.filteredItems; - expect(wrapper.findComponent(CloudAuthItem).exists()).toBeTruthy(); - expect(results.length == 1).toBeTruthy(); - - wrapper.vm.filter = "azure"; - results = wrapper.vm.filteredItems; - expect(results.length == 1).toBeTruthy(); - - wrapper.vm.filter = ""; - results = wrapper.vm.filteredItems; - expect(results.length == 2).toBeTruthy(); - }); - }); - - describe("create button", () => { - it("clicking create button should add a blank key", () => { - let results = wrapper.vm.filteredItems; - expect(wrapper.findComponent(CloudAuthItem).exists()).toBeTruthy(); - expect(results.length == 2).toBeTruthy(); - - const button = wrapper.find("button[name=createNewKey]"); - expect(button).toBeTruthy(); - button.trigger("click"); - - results = wrapper.vm.filteredItems; - expect(results.length == 3).toBeTruthy(); - - const blank = results.find((i) => i.id == null); - expect(blank).toBeTruthy(); - expect(blank.id == null).toBeTruthy(); - }); - }); -}); diff --git a/client/src/components/User/CloudAuth/CloudAuth.vue b/client/src/components/User/CloudAuth/CloudAuth.vue deleted file mode 100644 index 0a00e1d3c71..00000000000 --- a/client/src/components/User/CloudAuth/CloudAuth.vue +++ /dev/null @@ -1,372 +0,0 @@ - - - - - {{ errorMessage }} - - - - Manage Cloud Authorization - - - - - Instructions - - - - - Filter - - - - - Create New Key - - - - - - - - - See the online Documentation. - - - - - - - - - - - - - Create New Authorization Key - - - - - - - - - - - - - - - - - - - diff --git a/client/src/components/User/CloudAuth/CloudAuthItem.vue b/client/src/components/User/CloudAuth/CloudAuthItem.vue deleted file mode 100644 index 8c5526c3d0b..00000000000 --- a/client/src/components/User/CloudAuth/CloudAuthItem.vue +++ /dev/null @@ -1,93 +0,0 @@ - - - - - {{ credential.title }} - - - - - - Save Key - - - - - - Delete Key - - - - - Details - - - - - - - - - - - - - - diff --git a/client/src/components/User/CloudAuth/CredentialConfig.vue b/client/src/components/User/CloudAuth/CredentialConfig.vue deleted file mode 100644 index f9faaeef7b3..00000000000 --- a/client/src/components/User/CloudAuth/CredentialConfig.vue +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - - - - diff --git a/client/src/components/User/CloudAuth/CredentialForm.vue b/client/src/components/User/CloudAuth/CredentialForm.vue deleted file mode 100644 index 4c44c6fe692..00000000000 --- a/client/src/components/User/CloudAuth/CredentialForm.vue +++ /dev/null @@ -1,101 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/client/src/components/User/CloudAuth/index.js b/client/src/components/User/CloudAuth/index.js deleted file mode 100644 index 3e5bd9184df..00000000000 --- a/client/src/components/User/CloudAuth/index.js +++ /dev/null @@ -1 +0,0 @@ -export { default as CloudAuth } from "./CloudAuth.vue"; diff --git a/client/src/components/User/CloudAuth/model/AwsConfig.js b/client/src/components/User/CloudAuth/model/AwsConfig.js deleted file mode 100644 index 9c4f27164d6..00000000000 --- a/client/src/components/User/CloudAuth/model/AwsConfig.js +++ /dev/null @@ -1,28 +0,0 @@ -import { safeAssign } from "utils/safeAssign"; - -import { BaseModel } from "./BaseModel"; - -export class AwsConfig extends BaseModel { - constructor(props = {}) { - super(); - this.role_arn = ""; - safeAssign(this, props); - this.updateState(); - } -} - -AwsConfig.setValidator(function (model) { - const errors = {}; - if (!model.role_arn.length) { - errors.role_arn = "Missing role_arn"; - } - return errors; -}); - -AwsConfig.fields = { - role_arn: { - label: "Role ARN", - description: "The Amazon resource name (ARN) of the role to be assumed by Galaxy.", - placeholder: "arn:aws:iam::XXXXXXXXXXXX:role/XXXXXXXXXXX", - }, -}; diff --git a/client/src/components/User/CloudAuth/model/AwsConfig.test.js b/client/src/components/User/CloudAuth/model/AwsConfig.test.js deleted file mode 100644 index 938fd1974e6..00000000000 --- a/client/src/components/User/CloudAuth/model/AwsConfig.test.js +++ /dev/null @@ -1,22 +0,0 @@ -import { AwsConfig } from "./AwsConfig"; - -describe("AwsConfig", () => { - it("should instantiate", () => { - const instance = new AwsConfig(); - expect(instance).toBeTruthy(); - expect(!instance.dirty).toBeTruthy(); - expect(!instance.valid).toBeTruthy(); - }); - - it("should validate role_arn", () => { - const instance = new AwsConfig(); - instance.role_arn = "abc"; - expect(instance.fieldValid("role_arn")).toBeTruthy(); - }); - - it("should invalidate role_arn", () => { - const instance = new AwsConfig(); - instance.role_arn = ""; - expect(!instance.fieldValid("role_arn")).toBeTruthy(); - }); -}); diff --git a/client/src/components/User/CloudAuth/model/AzureConfig.js b/client/src/components/User/CloudAuth/model/AzureConfig.js deleted file mode 100644 index 800dd7ea334..00000000000 --- a/client/src/components/User/CloudAuth/model/AzureConfig.js +++ /dev/null @@ -1,62 +0,0 @@ -import { safeAssign } from "utils/safeAssign"; - -import { BaseModel } from "./BaseModel"; - -export class AzureConfig extends BaseModel { - constructor(props = {}) { - super(); - this.tenant_id = ""; - this.client_id = ""; - this.client_secret = ""; - safeAssign(this, props); - this.updateState(); - } -} - -AzureConfig.setValidator(function (model) { - const errors = {}; - - if (model.tenant_id.length < 36) { - errors.tenant_id = "Tenant ID too short"; - } - if (!model.tenant_id) { - errors.tenant_id = "Missing Tenant ID"; - } - - if (model.client_id.length < 36) { - errors.client_id = "Client ID too short"; - } - if (!model.client_id) { - errors.client_id = "Missing client ID"; - } - - if (!model.client_secret) { - errors.client_secret = "Missing secret"; - } - - return errors; -}); - -AzureConfig.fields = { - tenant_id: { - label: "Tenant ID", - mask: "********-****-****-****-************", - placeholder: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - maxlength: 36, - description: "Your Tenant ID (or Directory ID) on Azure.", - }, - client_id: { - label: "Client ID", - mask: "********-****-****-****-************", - placeholder: "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx", - maxlength: 36, - description: "The Client ID (or Application ID) you defined for Galaxy on your Azure directory.", - }, - client_secret: { - label: "Client Secret", - mask: "", - placeholder: "Client Secret", - description: - "A secret string you obtained from Azure portal that Galaxy can use to prove its identity when requesting tokens to access your resources.", - }, -}; diff --git a/client/src/components/User/CloudAuth/model/AzureConfig.test.js b/client/src/components/User/CloudAuth/model/AzureConfig.test.js deleted file mode 100644 index 35c4c966593..00000000000 --- a/client/src/components/User/CloudAuth/model/AzureConfig.test.js +++ /dev/null @@ -1,58 +0,0 @@ -import { AzureConfig } from "./AzureConfig"; - -describe("AzureConfig", () => { - it("should instantiate", () => { - const instance = new AzureConfig(); - expect(instance).toBeTruthy(); - expect(!instance.dirty).toBeTruthy(); - expect(!instance.valid).toBeTruthy(); - }); - - describe("client_secret", () => { - it("should validate client_secret", () => { - const instance = new AzureConfig(); - instance.client_secret = "abc"; - expect(instance.fieldValid("client_secret")).toBeTruthy(); - }); - - it("should invalidate client_secret", () => { - const instance = new AzureConfig(); - instance.client_secret = ""; - expect(!instance.fieldValid("client_secret")).toBeTruthy(); - }); - }); - - describe("tenant_id", () => { - it("should validate tenant_id", () => { - const validTenantId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; - const instance = new AzureConfig(); - instance.tenant_id = validTenantId; - expect(instance.fieldValid("tenant_id")).toBeTruthy(); - }); - - it("should invalidate tenant_id", () => { - const instance = new AzureConfig(); - instance.tenant_id = ""; - expect(!instance.fieldValid("tenant_id")).toBeTruthy(); - instance.tenant_id = "asdfa"; - expect(!instance.fieldValid("tenant_id")).toBeTruthy(); - }); - }); - - describe("client_id", () => { - it("should validate client_id", () => { - const validClientId = "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx"; - const instance = new AzureConfig(); - instance.client_id = validClientId; - expect(instance.fieldValid("client_id")).toBeTruthy(); - }); - - it("should invalidate client_id", () => { - const instance = new AzureConfig(); - instance.client_id = ""; - expect(!instance.fieldValid("client_id")).toBeTruthy(); - instance.client_id = "asdf"; - expect(!instance.fieldValid("client_id")).toBeTruthy(); - }); - }); -}); diff --git a/client/src/components/User/CloudAuth/model/BaseModel.js b/client/src/components/User/CloudAuth/model/BaseModel.js deleted file mode 100644 index 5dede7517de..00000000000 --- a/client/src/components/User/CloudAuth/model/BaseModel.js +++ /dev/null @@ -1,102 +0,0 @@ -const lastState = new WeakMap(); -const validators = new WeakMap(); -const transients = new WeakMap(); -const counter = new WeakMap(); - -// Helps with Vue for-loop keys -let instanceCounter = 0; - -export class BaseModel { - constructor() { - counter.set(this, instanceCounter++); - } - - /* object ID */ - - get counter() { - return counter.get(this); - } - - /* Dirty state tracking */ - - get dirty() { - return lastState.get(this) != this.state; - } - - get clean() { - return !this.dirty; - } - - get state() { - // build state JSON ignoring transient fields - const tFields = this.transientFields; - return JSON.stringify(this, function (key) { - if (!tFields.has(key)) { - return this[key]; - } - return undefined; - }); - } - - get lastState() { - return lastState.get(this); - } - - get transientFields() { - const key = this.constructor; - if (!transients.has(key)) { - transients.set(key, new Set()); - } - return transients.get(key); - } - - updateState() { - lastState.set(this, this.state); - } - - // Setting a property name as transient for a class means its - // state will be caclulated without regard to that propety - static setTransient(...fieldNames) { - const klass = this; // this will be a class - if (!transients.has(klass)) { - transients.set(klass, new Set()); - } - - const fields = transients.get(klass); - fieldNames.forEach((fieldName) => fields.add(fieldName)); - transients.set(klass, fields); - } - - /* Validation */ - - get valid() { - return Object.keys(this.validationErrors).length == 0; - } - - get validationErrors() { - return this.constructor.validate(this); - } - - errorMessage(field) { - if (field in this.validationErrors) { - return this.validationErrors[field]; - } - return "errmessage"; - } - - fieldValid(fieldName) { - return !(fieldName in this.validationErrors); - } - - static validate(model) { - if (validators.has(this)) { - const validator = validators.get(this); - return validator(model); - } - throw new Error("Missing validator"); - } - - static setValidator(validationFunction) { - validators.set(this, validationFunction); - } -} diff --git a/client/src/components/User/CloudAuth/model/Credential.js b/client/src/components/User/CloudAuth/model/Credential.js deleted file mode 100644 index 6e5470d2bd0..00000000000 --- a/client/src/components/User/CloudAuth/model/Credential.js +++ /dev/null @@ -1,105 +0,0 @@ -import { safeAssign } from "utils/safeAssign"; - -import { BaseModel } from "./BaseModel"; -import { ResourceProviders } from "./ResourceProviders"; - -export class Credential extends BaseModel { - constructor(props = {}) { - super(); - - this.id = null; - this.description = ""; - this.authn_id = null; // identity provider - this.provider = null; // resource provider - - // transient props, exclude from state - this.expanded = false; - this.loading = false; - - // populate props - const options = Object.assign({}, Credential.defaults, props); - safeAssign(this, options); - - // init nested config - this.config = new this.configClass(options.config); - - // initialize state - this.updateState(); - } - - get title() { - return this.description.length ? this.description : this.provider; - } - - get valid() { - return super.valid && this.config.valid; - } - - // Alias for provider also changes config object when updated - // Set this when updating in the UI - - get resourceProvider() { - return this.provider; - } - - set resourceProvider(newProvider) { - this.provider = newProvider; - this.config = new this.configClass({}); - } - - // Polymorphic config class - - get configClass() { - return ResourceProviders.get(this.provider).klass; - } - - // Methods - - match(searchText = "") { - // TODO: more robust object matching? - return searchText.length ? this.title.includes(searchText) : true; - } - - // Statics - - static get defaults() { - return { - authn_id: null, - provider: "aws", - expanded: false, - }; - } - - static create(props = {}) { - return new Credential(props); - } -} - -/** - * Transient fields do not factor into the state - * for purposes of dirty tracking - */ -Credential.setTransient("expanded", "loading"); - -/** - * A validator function's job is to return an object - * where the keys are field names and the values are - * error messages. - */ -Credential.setValidator(function (model) { - const errors = {}; - - if (!model.provider) { - errors.provider = "Provider must be set"; - } - - if (!model.authn_id) { - errors.authn_id = "Please pick an identity provider"; - } - - if (!model.config.valid) { - errors.config = "Invalid config object"; - } - - return errors; -}); diff --git a/client/src/components/User/CloudAuth/model/Credential.test.js b/client/src/components/User/CloudAuth/model/Credential.test.js deleted file mode 100644 index 91978505d94..00000000000 --- a/client/src/components/User/CloudAuth/model/Credential.test.js +++ /dev/null @@ -1,106 +0,0 @@ -import { Credential } from "./Credential"; - -describe("Credential model", () => { - describe("basic model props", () => { - it("should exist", () => { - expect(Credential).toBeTruthy(); - }); - - it("should build with defaults", () => { - const instance = new Credential(); - expect(instance).toBeTruthy(); - }); - - it("should build with props", () => { - const description = "i am the test description"; - const props = { description }; - const instance = new Credential(props); - expect(instance).toBeTruthy(); - expect(instance.description == description).toBeTruthy(); - }); - - it("default config object should be AWS", () => { - const instance = new Credential(); - expect("role_arn" in instance.config).toBeTruthy(); - expect(instance.config.constructor.name == "AwsConfig").toBeTruthy(); - }); - - it("should load a different config object given the right provider", () => { - const provider = "azure"; - const props = { provider }; - const instance = new Credential(props); - expect(instance.provider == provider).toBeTruthy(); - expect(instance.config.constructor.name == "AzureConfig").toBeTruthy(); - }); - - it("should dynamically switch config objects as resourceProvider is changed", () => { - const instance = new Credential(); - expect(instance.provider == "aws").toBeTruthy(); - instance.resourceProvider = "azure"; - expect(instance.config.constructor.name == "AzureConfig").toBeTruthy(); - instance.resourceProvider = "aws"; - expect(instance.config.constructor.name == "AwsConfig").toBeTruthy(); - }); - }); - - describe("dirty state", () => { - it("should flag as dirty when a prop is changed", () => { - const instance = new Credential(); - instance.description = "foo"; - expect(instance.dirty).toBeTruthy(); - }); - - it("should flag as clean when a prop is restored", () => { - const instance = new Credential(); - instance.description = "foo"; - expect(instance.dirty).toBeTruthy(); - instance.description = ""; - expect(!instance.dirty).toBeTruthy(); - }); - - it("should not flag as dirty when a transient prop is changed", () => { - const instance = new Credential(); - instance.loading = true; - expect(!instance.dirty).toBeTruthy(); - }); - }); - - describe("validation", () => { - it("a new object should be invalid", () => { - const instance = new Credential(); - expect(!instance.valid).toBeTruthy(); - }); - - it("it should become valid when props are assigned", () => { - const instance = new Credential(); - instance.authn_id = "floob"; - instance.resourceProvider = "aws"; - instance.config.role_arn = "abc"; - expect(instance.valid).toBeTruthy(); - }); - - it("it should be valid when initialized with correct props", () => { - const props = { - authn_id: "asdfasdf", - provider: "aws", - config: { - role_arn: "floobar", - }, - }; - const instance = new Credential(props); - expect(instance.valid).toBeTruthy(); - }); - - it("should be invalid when bad props assigned", () => { - const props = { - authn_id: "asdfasdf", - provider: "aws", - config: { - role_arn: "", - }, - }; - const instance = new Credential(props); - expect(!instance.valid).toBeTruthy(); - }); - }); -}); diff --git a/client/src/components/User/CloudAuth/model/IdentityProvider.js b/client/src/components/User/CloudAuth/model/IdentityProvider.js deleted file mode 100644 index 7c0974fc50b..00000000000 --- a/client/src/components/User/CloudAuth/model/IdentityProvider.js +++ /dev/null @@ -1,29 +0,0 @@ -import { safeAssign } from "utils/safeAssign"; - -export class IdentityProvider { - constructor(props = {}) { - this.id = ""; - this.provider = ""; - safeAssign(this, props); - } - - // Aliases - - get authn_id() { - return this.id; - } - - get text() { - return this.provider; - } - - get value() { - return this.id; - } - - // Statics - - static create(props = {}) { - return new IdentityProvider(props); - } -} diff --git a/client/src/components/User/CloudAuth/model/ResourceProviders.js b/client/src/components/User/CloudAuth/model/ResourceProviders.js deleted file mode 100644 index cefcbb63faa..00000000000 --- a/client/src/components/User/CloudAuth/model/ResourceProviders.js +++ /dev/null @@ -1,14 +0,0 @@ -import { AwsConfig } from "./AwsConfig"; -import { AzureConfig } from "./AzureConfig"; - -export const ResourceProviders = new Map(); - -ResourceProviders.set("aws", { - klass: AwsConfig, - label: "Amazon Web Services (AWS)", -}); - -ResourceProviders.set("azure", { - klass: AzureConfig, - label: "Microsoft Azure", -}); diff --git a/client/src/components/User/CloudAuth/model/index.js b/client/src/components/User/CloudAuth/model/index.js deleted file mode 100644 index 24f019b67c5..00000000000 --- a/client/src/components/User/CloudAuth/model/index.js +++ /dev/null @@ -1,3 +0,0 @@ -export { Credential } from "./Credential"; -export { IdentityProvider } from "./IdentityProvider"; -export { ResourceProviders } from "./ResourceProviders"; diff --git a/client/src/components/User/CloudAuth/model/service.js b/client/src/components/User/CloudAuth/model/service.js deleted file mode 100644 index e5cdb03a66d..00000000000 --- a/client/src/components/User/CloudAuth/model/service.js +++ /dev/null @@ -1,81 +0,0 @@ -/** - * Data retrieval/storage for the auth keys - */ - -import axios from "axios"; -import { getRootFromIndexLink } from "onload"; - -import { Credential, IdentityProvider } from "./index"; - -const getUrl = (path) => getRootFromIndexLink() + path; - -export async function listCredentials() { - const url = getUrl("api/cloud/authz"); - const response = await axios.get(url); - if (response.status != 200) { - throw new Error("Unexpected response from listing."); - } - return response.data.map(Credential.create); -} - -export async function getCredential(id) { - const url = getUrl("api/cloud/authz/${id}"); - const response = await axios.get(url); - if (response.status != 200) { - throw new Error("Unexpected response loading key."); - } - return Credential.create(response.data); -} - -export async function saveCredential(newItem) { - const model = Credential.create(newItem); - const response = await saveOrUpdate(model); - if (response.status != 200) { - throw new Error("Save failure."); - } - return Credential.create(response.data); -} - -async function saveOrUpdate(model) { - return model.id - ? axios.put(getUrl(`api/cloud/authz/${model.id}`), model) - : axios.post(getUrl("api/cloud/authz"), model); -} - -export async function deleteCredential(doomed) { - const model = Credential.create(doomed); - if (model.id) { - const url = getUrl(`api/cloud/authz/${doomed.id}`); - const response = await axios.delete(url); - if (response.status != 200) { - throw new Error("Delete failure."); - } - } - return model; -} - -// Memoize results (basically never changes) - -let identityProviders; - -export async function getIdentityProviders() { - if (!identityProviders) { - const url = getUrl("authnz"); - const response = await axios.get(url); - if (response.status != 200) { - throw new Error("Unable to load identity providers"); - } - // This should be idempotent (and safe). - // eslint-disable-next-line require-atomic-updates - identityProviders = response.data.map(IdentityProvider.create); - } - return identityProviders; -} - -export default { - listCredentials, - getCredential, - saveCredential, - deleteCredential, - getIdentityProviders, -}; diff --git a/client/src/components/User/CloudAuth/testdata/listCredentials.json b/client/src/components/User/CloudAuth/testdata/listCredentials.json deleted file mode 100644 index 82973dc3a7f..00000000000 --- a/client/src/components/User/CloudAuth/testdata/listCredentials.json +++ /dev/null @@ -1,32 +0,0 @@ -[ - { - "authn_id": "f2db41e1fa331b3e", - "user_id": "f2db41e1fa331b3e", - "description": "aws description", - "last_update": "2019-04-04 09:13:16.135032", - "last_activity": "2019-04-04 09:13:16.135042", - "create_time": "2019-04-04 16:13:16.135755", - "provider": "aws", - "model_class": "CloudAuthz", - "config": { - "role_arn": "floobadooba" - }, - "id": "a7db2fac67043c7e" - }, - { - "authn_id": "f2db41e1fa331b3e", - "user_id": "f2db41e1fa331b3e", - "description": "azure description", - "last_update": "2019-04-03 20:21:51.350748", - "last_activity": "2019-04-03 20:21:51.350804", - "create_time": "2019-04-04 03:21:51.351508", - "provider": "azure", - "model_class": "CloudAuthz", - "config": { - "client_secret": "dannon likes Nickleback", - "client_id": "22222222-2222-2222-2222-222222222222", - "tenant_id": "11111111-1111-1111-1111-111111111111" - }, - "id": "b472e2eb553fa0d1" - } -] \ No newline at end of file
See the online Documentation.