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
3 changes: 2 additions & 1 deletion addons/api/addon/generated/models/credential-library.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,9 +81,10 @@ export default class GeneratedCredentialLibraryModel extends BaseModel {
})
credential_type;

@attr('object-as-array', {
@attr('object', {
for: 'vault-generic',
description: 'It indicates the credential mapping overrides.',
emptyObjectIfMissing: true,
})
credential_mapping_overrides;

Expand Down
4 changes: 2 additions & 2 deletions addons/api/addon/generated/models/credential.js
Original file line number Diff line number Diff line change
Expand Up @@ -59,9 +59,9 @@ export default class GeneratedCredentialModel extends BaseModel {
})
username;

// =attributes (username_password, username_password_domain)
// =attributes (username_password, username_password_domain, password)
@attr('string', {
for: ['username_password', 'username_password_domain'],
for: ['username_password', 'username_password_domain', 'password'],
isNestedAttribute: true,
isSecret: true,
description: 'The password for credential.',
Expand Down
3 changes: 3 additions & 0 deletions addons/api/addon/models/credential-library.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
TYPE_CREDENTIAL_USERNAME_PASSWORD,
TYPE_CREDENTIAL_PASSWORD,
} from 'api/models/credential';
/**
* Enum options for credential library.
Expand All @@ -19,6 +20,7 @@ export const options = {
TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
TYPE_CREDENTIAL_USERNAME_PASSWORD,
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
TYPE_CREDENTIAL_PASSWORD,
],
mapping_overrides: {
username_password: ['username_attribute', 'password_attribute'],
Expand All @@ -32,6 +34,7 @@ export const options = {
'password_attribute',
'domain_attribute',
],
password: ['password_attribute'],
},
};

Expand Down
5 changes: 4 additions & 1 deletion addons/api/addon/models/credential.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,13 +12,16 @@ import GeneratedCredentialModel from '../generated/models/credential';
export const TYPE_CREDENTIAL_USERNAME_PASSWORD = 'username_password';
export const TYPE_CREDENTIAL_SSH_PRIVATE_KEY = 'ssh_private_key';
export const TYPE_CREDENTIAL_JSON = 'json';
export const TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN = 'username_password_domain';
export const TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN =
'username_password_domain';
export const TYPE_CREDENTIAL_PASSWORD = 'password';

export const TYPES_CREDENTIAL = Object.freeze([
TYPE_CREDENTIAL_USERNAME_PASSWORD,
TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
TYPE_CREDENTIAL_JSON,
TYPE_CREDENTIAL_PASSWORD,
]);

export default class CredentialModel extends GeneratedCredentialModel {
Expand Down
4 changes: 3 additions & 1 deletion addons/api/addon/serializers/credential-library.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,9 @@ export default class CredentialLibrarySerializer extends ApplicationSerializer {
*/
handleCredentialMappingOverrides(serialized, isNew) {
const { credential_type, credential_mapping_overrides } = serialized;

if (Object.keys(credential_mapping_overrides).length === 0) {
serialized.credential_mapping_overrides = null;
}
// API expects to send null to fields if it is undefined or deleted
if (credential_mapping_overrides && !isNew) {
serialized.credential_mapping_overrides = options.mapping_overrides[
Expand Down
2 changes: 1 addition & 1 deletion addons/api/mirage/factories/credential-store.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export default factory.extend({
break;
case 'static':
default:
server.createList('credential', 3, {
server.createList('credential', 5, {
scope,
credentialStore,
});
Expand Down
12 changes: 8 additions & 4 deletions addons/api/mirage/factories/credential.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ import {
TYPE_CREDENTIAL_SSH_PRIVATE_KEY,
TYPE_CREDENTIAL_USERNAME_PASSWORD,
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
TYPE_CREDENTIAL_JSON,
TYPE_CREDENTIAL_PASSWORD,
} from 'api/models/credential';

const types = [...TYPES_CREDENTIAL];
Expand All @@ -20,14 +22,16 @@ export default factory.extend({
type: (i) => types[i % types.length],
id() {
switch (this.type) {
case 'ssh_private_key':
case TYPE_CREDENTIAL_SSH_PRIVATE_KEY:
return generatedId('credspk_');
case 'username_password':
case TYPE_CREDENTIAL_USERNAME_PASSWORD:
return generatedId('credup_');
case 'json':
case TYPE_CREDENTIAL_JSON:
return generatedId('credjson_');
case 'username_password_domain':
case TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN:
return generatedId('credupd_');
case TYPE_CREDENTIAL_PASSWORD:
return generatedId('credp_');
}
},
authorized_actions: () =>
Expand Down
18 changes: 12 additions & 6 deletions addons/api/mirage/factories/target.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,9 @@ import {
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
TYPE_CREDENTIAL_USERNAME_PASSWORD,
TYPE_CREDENTIAL_JSON,
TYPE_CREDENTIAL_PASSWORD,
} from 'api/models/credential';
import { TYPE_CREDENTIAL_LIBRARY_VAULT_LDAP } from 'api/models/credential-library';

const randomBoolean = (chance = 0.5) => Math.random() < chance;
const randomFilter = () =>
Expand Down Expand Up @@ -111,14 +113,17 @@ export default factory.extend({
server.schema.credentialLibraries,
(cred) =>
cred.scopeId === scope.id &&
cred.credential_type !== TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
cred.credential_type !== TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN &&
cred.credential_type !== TYPE_CREDENTIAL_PASSWORD &&
cred.type !== TYPE_CREDENTIAL_LIBRARY_VAULT_LDAP,
);
const filteredCredentials = selectItems(
server.schema.credentials,
(cred) =>
cred.scopeId === scope.id &&
![
TYPE_CREDENTIAL_JSON,
TYPE_CREDENTIAL_PASSWORD,
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
].includes(cred.type),
);
Expand All @@ -127,11 +132,12 @@ export default factory.extend({
const filteredCredentialLibrariesForRDP = selectItems(
server.schema.credentialLibraries,
(cred) =>
cred.scopeId === scope.id &&
[
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
TYPE_CREDENTIAL_USERNAME_PASSWORD,
].includes(cred.credential_type),
(cred.scopeId === scope.id &&
[
TYPE_CREDENTIAL_USERNAME_PASSWORD_DOMAIN,
TYPE_CREDENTIAL_USERNAME_PASSWORD,
].includes(cred.credential_type)) ||
cred.type == TYPE_CREDENTIAL_LIBRARY_VAULT_LDAP,
);
const filteredCredentialsForRDP = selectItems(
server.schema.credentials,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,9 +25,7 @@ module('Unit | Serializer | credential library', function (hooks) {
http_method: 'GET',
version: 1,
credential_type: 'ssh_private_key',
credential_mapping_overrides: [
{ key: 'username_attribute', value: 'user' },
],
credential_mapping_overrides: { username_attribute: 'user' },
});
const snapshot = record._createSnapshot();
const serializedRecord = serializer.serialize(snapshot);
Expand Down Expand Up @@ -64,9 +62,7 @@ module('Unit | Serializer | credential library', function (hooks) {
http_method: 'GET',
version: 1,
credential_type: 'ssh_private_key',
credential_mapping_overrides: [
{ key: 'private_key_attribute', value: 'test' },
],
credential_mapping_overrides: { private_key_attribute: 'test' },
},
},
});
Expand Down
15 changes: 9 additions & 6 deletions addons/core/translations/resources/en-us.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -913,11 +913,12 @@ credential-library:
ssh_private_key: SSH Private Key
username_password: Username & Password
username_password_domain: Username, Password & Domain
username_attribute: Username
password_attribute: Password
private_key_passphrase_attribute: Private Key Passphrase
private_key_attribute: Private Key
domain_attribute: Domain
password: Password
username_attribute: username_attribute
password_attribute: password_attribute
private_key_passphrase_attribute: private_key_passphrase_attribute
private_key_attribute: private_key_attribute
domain_attribute: domain_attribute
actions:
create: New Credential Library
delete: Delete Credential Library
Expand Down Expand Up @@ -989,12 +990,14 @@ credential:
ssh_private_key: Username & Key Pair
username_password_domain: Username, Password & Domain
json: JSON
password: Password
unknown: Unknown
help:
username_password: Connect using the provided username and password.
ssh_private_key: Connect using a username, public key, and private key.
json: Connect using a JSON blob containing the credentials.
username_password_domain: Includes a domain field for Active Directory
username_password_domain: Includes a domain field for Active Directory.
password: Connect using only a provided password.
form:
username:
label: Username
Expand Down
13 changes: 13 additions & 0 deletions addons/rose/addon/styles/hds/themes/dark-mode/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -121,14 +121,27 @@
}
}

@mixin dark-mode-ember-dropdown-override {
.ember-basic-dropdown-content {
background-color: var(--token-color-palette-neutral-0);

/* stylelint-disable-next-line selector-class-pattern */
&.ember-basic-dropdown-content--in-place {
box-shadow: none;
}
}
}

@media (prefers-color-scheme: dark) {
.ember-application:not(.rose-theme-light) {
@include dark-mode;
@include dark-mode-modal-override;
@include dark-mode-ember-dropdown-override;
}
}

.ember-application.rose-theme-dark {
@include dark-mode;
@include dark-mode-modal-override;
@include dark-mode-ember-dropdown-override;
}
30 changes: 13 additions & 17 deletions e2e-tests/admin/pages/credential-stores.js
Original file line number Diff line number Diff line change
Expand Up @@ -147,11 +147,11 @@ export class CredentialStoresPage extends BaseResourcePage {
await this.page.getByRole('link', { name: 'New Credential' }).click();
}

await this.page.getByLabel('Name (Optional)').fill(credentialName);
await this.page.getByLabel('Name', { exact: true }).fill(credentialName);
await this.page.getByLabel('Description').fill('This is an automated test');
await this.page.getByRole('combobox', { name: 'Type' }).click();
await this.page
.getByRole('group', { name: 'Type' })
.getByLabel('Username & Key Pair')
.getByRole('option', { name: 'Username & Key Pair' })
.click();
await this.page
.getByLabel('Username Required', { exact: true })
Expand Down Expand Up @@ -207,11 +207,11 @@ export class CredentialStoresPage extends BaseResourcePage {
}

const credentialName = 'Credential ' + nanoid();
await this.page.getByLabel('Name (Optional)').fill(credentialName);
await this.page.getByLabel('Name', { exact: true }).fill(credentialName);
await this.page.getByLabel('Description').fill('This is an automated test');
await this.page.getByRole('combobox', { name: 'Type' }).click();
await this.page
.getByRole('group', { name: 'Type' })
.getByLabel('Username & Password')
.getByRole('option', { name: 'Username & Password' })
.click();
await this.page
.getByLabel('Username Required', { exact: true })
Expand Down Expand Up @@ -271,11 +271,11 @@ export class CredentialStoresPage extends BaseResourcePage {
}

const credentialName = 'Credential ' + nanoid();
await this.page.getByLabel('Name (Optional)').fill(credentialName);
await this.page.getByLabel('Name', { exact: true }).fill(credentialName);
await this.page.getByLabel('Description').fill('This is an automated test');
await this.page.getByRole('combobox', { name: 'Type' }).click();
await this.page
.getByRole('group', { name: 'Type' })
.getByLabel('Username, Password & Domain')
.getByRole('option', { name: 'Username, Password & Domain' })
.click();
await this.page
.getByLabel('Username Required', { exact: true })
Expand Down Expand Up @@ -308,11 +308,9 @@ export class CredentialStoresPage extends BaseResourcePage {
await this.page.getByRole('link', { name: 'Credential Libraries' }).click();
await this.page.getByRole('link', { name: 'New', exact: true }).click();
await this.page
.getByLabel('Name (Optional)', { exact: true })
.getByLabel('Name', { exact: true })
.fill(credentialLibraryName);
await this.page
.getByLabel('Description (Optional)')
.fill('This is an automated test');
await this.page.getByLabel('Description').fill('This is an automated test');
await this.page
.getByRole('group', { name: 'Type' })
.getByLabel('Generic Secrets')
Expand Down Expand Up @@ -345,11 +343,9 @@ export class CredentialStoresPage extends BaseResourcePage {
await this.page.getByRole('link', { name: 'New', exact: true }).click();

await this.page
.getByLabel('Name (Optional)', { exact: true })
.getByLabel('Name', { exact: true })
.fill(credentialLibraryName);
await this.page
.getByLabel('Description (Optional)')
.fill('This is an automated test');
await this.page.getByLabel('Description').fill('This is an automated test');
await this.page
.getByRole('group', { name: 'Type' })
.getByLabel('SSH Certificates')
Expand Down
8 changes: 3 additions & 5 deletions e2e-tests/admin/tests/credential-store-static.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -191,12 +191,10 @@ test(
.getByRole('link', { name: 'Credentials', exact: true })
.click();
await page.getByRole('link', { name: 'New', exact: true }).click();
await page.getByLabel('Name (Optional)').fill(credentialName);
await page.getByLabel('Name', { exact: true }).fill(credentialName);
await page.getByLabel('Description').fill('This is an automated test');
await page
.getByRole('group', { name: 'Type' })
.getByLabel('JSON')
.click();
await page.getByRole('combobox', { name: 'Type' }).click();
await page.getByRole('option', { name: 'JSON' }).click();
await page.getByText('{}').click();
const testName = 'name-json';
const testPassword = 'password-json';
Expand Down
6 changes: 2 additions & 4 deletions e2e-tests/admin/tests/credential-store-vault.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -99,11 +99,9 @@ test(
await page.getByRole('link', { name: 'Credential Libraries' }).click();
await page.getByRole('link', { name: 'New', exact: true }).click();
await page
.getByLabel('Name (Optional)', { exact: true })
.getByLabel('Name', { exact: true })
.fill(credentialLibraryName);
await page
.getByLabel('Description (Optional)')
.fill('This is an automated test');
await page.getByLabel('Description').fill('This is an automated test');
await page
.getByLabel('Vault Path')
.fill(`${secretsPath}/data/${secretName}`);
Expand Down
Loading
Loading