-
- <:leftIcon>
-
-
-
- <:default>{{t 'back'}}
-
-
-
-
- <:actionBtn as |ab|>
- {{#if this.showOrgNameActionBtn}}
-
- <:leftIcon>
-
-
-
- <:default>
- {{t 'editName'}}
-
-
- {{/if}}
-
-
-
-
{{#if this.me.org.is_owner}}
diff --git a/app/components/organization/settings/index.js b/app/components/organization/settings/index.js
new file mode 100644
index 0000000000..a866970306
--- /dev/null
+++ b/app/components/organization/settings/index.js
@@ -0,0 +1,6 @@
+import Component from '@glimmer/component';
+import { inject as service } from '@ember/service';
+
+export default class OrganizationSettings extends Component {
+ @service me;
+}
diff --git a/app/components/organization/settings/index.scss b/app/components/organization/settings/index.scss
new file mode 100644
index 0000000000..5bfc454f1d
--- /dev/null
+++ b/app/components/organization/settings/index.scss
@@ -0,0 +1,6 @@
+.org-settings-container {
+ background-color: var(--background-main);
+ margin-top: 1.5em;
+ padding: 1.5em 2em 5em 2em;
+ box-sizing: border-box;
+}
diff --git a/app/controllers/authenticated/dashboard/organization-settings/service-account.ts b/app/controllers/authenticated/dashboard/organization-settings/service-account.ts
new file mode 100644
index 0000000000..75728b6daf
--- /dev/null
+++ b/app/controllers/authenticated/dashboard/organization-settings/service-account.ts
@@ -0,0 +1,9 @@
+import Controller from '@ember/controller';
+
+export default class AuthenticatedDashboardOrganizationSettingsServiceAccountController extends Controller {
+ queryParams = ['sa_limit', 'sa_offset', 'show_system_created'];
+
+ sa_limit = 10;
+ sa_offset = 0;
+ show_system_created = false;
+}
diff --git a/app/models/service-account.ts b/app/models/service-account.ts
new file mode 100644
index 0000000000..e2639bc5e8
--- /dev/null
+++ b/app/models/service-account.ts
@@ -0,0 +1,63 @@
+import Model, { attr, belongsTo, type AsyncBelongsTo } from '@ember-data/model';
+import type UserModel from './user';
+
+export enum ServiceAccountType {
+ USER = 1,
+ SYSTEM = 2,
+}
+
+export default class ServiceAccountModel extends Model {
+ @attr('boolean')
+ declare scopePublicApiUserRead: boolean;
+
+ @attr('boolean')
+ declare scopePublicApiProjectRead: boolean;
+
+ @attr('boolean')
+ declare scopePublicApiScanResultVa: boolean;
+
+ @attr('boolean')
+ declare isExpired: boolean;
+
+ @attr('boolean')
+ declare isActive: boolean;
+
+ @attr('boolean')
+ declare allProjects: boolean;
+
+ @attr('date')
+ declare expiry: Date;
+
+ @attr('string')
+ declare name: string;
+
+ @attr('string')
+ declare description: string;
+
+ @attr('string')
+ declare accessKeyId: string;
+
+ @attr('string')
+ declare secretAccessKey: string;
+
+ @attr('number')
+ declare serviceAccountType: ServiceAccountType;
+
+ @attr('date')
+ declare updatedOn: Date;
+
+ @attr('date')
+ declare createdOn: Date;
+
+ @belongsTo('user')
+ declare updatedByUser: AsyncBelongsTo
| null;
+
+ @belongsTo('user')
+ declare createdByUser: AsyncBelongsTo;
+}
+
+declare module 'ember-data/types/registries/model' {
+ export default interface ModelRegistry {
+ 'service-account': ServiceAccountModel;
+ }
+}
diff --git a/app/router.ts b/app/router.ts
index 99688302ee..de6b68741e 100644
--- a/app/router.ts
+++ b/app/router.ts
@@ -231,7 +231,14 @@ Router.map(function () {
this.route('teams');
});
- this.route('organization-settings', { path: '/organization/settings' });
+ this.route(
+ 'organization-settings',
+ { path: '/organization/settings' },
+ function () {
+ this.route('index', { path: '/' });
+ this.route('service-account');
+ }
+ );
});
}
);
diff --git a/app/routes/authenticated/dashboard/organization-settings.ts b/app/routes/authenticated/dashboard/organization-settings.ts
index cde29c1fe7..5295b76cd8 100644
--- a/app/routes/authenticated/dashboard/organization-settings.ts
+++ b/app/routes/authenticated/dashboard/organization-settings.ts
@@ -1,21 +1,16 @@
-import Store from '@ember-data/store';
-import Route from '@ember/routing/route';
-import RouterService from '@ember/routing/router-service';
import { inject as service } from '@ember/service';
+import Route from '@ember/routing/route';
+import type RouterService from '@ember/routing/router-service';
-import UserModel from 'irene/models/user';
-import MeService from 'irene/services/me';
-import OrganizationService from 'irene/services/organization';
-
-type AjaxError = { status: number };
+import type UserModel from 'irene/models/user';
+import type OrganizationModel from 'irene/models/organization';
+import type MeService from 'irene/services/me';
+import type OrganizationService from 'irene/services/organization';
export default class AuthenticatedOrganizationSettingsRoute extends Route {
@service declare me: MeService;
@service declare organization: OrganizationService;
- @service('notifications') declare notify: NotificationService;
@service declare router: RouterService;
- @service declare store: Store;
- @service ajax!: any;
beforeModel(): void {
if (!this.me.org?.get('is_admin')) {
@@ -26,30 +21,9 @@ export default class AuthenticatedOrganizationSettingsRoute extends Route {
}
async model() {
- const url = `/api/organizations/${this.organization.selected?.id}/github`;
- let integratedUser: unknown | null = null;
- let reconnect = false;
-
- try {
- const data = await this.ajax.request(url);
-
- if (data) {
- integratedUser = data;
- }
- } catch (err) {
- if ((err as AjaxError).status === 400) {
- reconnect = true;
- }
- }
-
- await this.store.query('organization', { id: null });
-
return {
- integratedUser,
- reconnect,
- user: (await this.modelFor('authenticated')) as UserModel,
- organization: await this.organization.selected,
- me: this.me,
+ user: this.modelFor('authenticated') as UserModel,
+ organization: this.organization.selected as OrganizationModel,
};
}
}
diff --git a/app/routes/authenticated/dashboard/organization-settings/index.ts b/app/routes/authenticated/dashboard/organization-settings/index.ts
new file mode 100644
index 0000000000..a031ad4a64
--- /dev/null
+++ b/app/routes/authenticated/dashboard/organization-settings/index.ts
@@ -0,0 +1,41 @@
+import Route from '@ember/routing/route';
+import { service } from '@ember/service';
+
+import type MeService from 'irene/services/me';
+import type { ModelFrom } from 'irene/utils/types';
+import type AuthenticatedOrganizationSettingsRoute from '../organization-settings';
+
+export default class AuthenticatedDashboardOrganizationSettingsIndexRoute extends Route {
+ @service declare me: MeService;
+ @service declare ajax: any;
+
+ async model() {
+ const { organization, user } = this.modelFor(
+ 'authenticated.dashboard.organization-settings'
+ ) as ModelFrom;
+
+ const url = `/api/organizations/${organization.get('id')}/github`;
+ let integratedUser: unknown | null = null;
+ let reconnect = false;
+
+ try {
+ const data = await this.ajax.request(url);
+
+ if (data) {
+ integratedUser = data;
+ }
+ } catch (err) {
+ if ((err as AjaxError).status === 400) {
+ reconnect = true;
+ }
+ }
+
+ return {
+ integratedUser,
+ reconnect,
+ user,
+ organization,
+ me: this.me,
+ };
+ }
+}
diff --git a/app/routes/authenticated/dashboard/organization-settings/service-account.ts b/app/routes/authenticated/dashboard/organization-settings/service-account.ts
new file mode 100644
index 0000000000..034bb49682
--- /dev/null
+++ b/app/routes/authenticated/dashboard/organization-settings/service-account.ts
@@ -0,0 +1,29 @@
+import Route from '@ember/routing/route';
+
+export interface OrganizationSettingsServiceAccountRouteQueryParams {
+ sa_limit: number;
+ sa_offset: number;
+ show_system_created: boolean;
+}
+
+export default class AuthenticatedDashboardOrganizationSettingsServiceAccountRoute extends Route {
+ queryParams = {
+ sa_limit: {
+ refreshModel: true,
+ },
+ sa_offset: {
+ refreshModel: true,
+ },
+ show_system_created: {
+ refreshModel: true,
+ },
+ };
+
+ model(
+ queryParams: Partial
+ ) {
+ return {
+ queryParams,
+ };
+ }
+}
diff --git a/app/styles/_component-variables.scss b/app/styles/_component-variables.scss
index d43197f860..8336373db7 100644
--- a/app/styles/_component-variables.scss
+++ b/app/styles/_component-variables.scss
@@ -193,14 +193,26 @@ body {
--ak-chip-color-outlined-warn: var(--warn-main);
--ak-chip-color-outlined-info: var(--info-main);
- --ak-chip-color-semi-filled-outlined-default-background: var(--neutral-grey-200);
- --ak-chip-color-semi-filled-outlined-default-contrast-text: var(--neutral-grey-700);
+ --ak-chip-color-semi-filled-outlined-default-background: var(
+ --neutral-grey-200
+ );
+ --ak-chip-color-semi-filled-outlined-default-contrast-text: var(
+ --neutral-grey-700
+ );
--ak-chip-color-semi-filled-outlined-primary-background: var(--primary-light);
- --ak-chip-color-semi-filled-outlined-primary-contrast-text: var(--primary-dark);
- --ak-chip-color-semi-filled-outlined-secondary-background: var(--neutral-grey-200);
- --ak-chip-color-semi-filled-outlined-secondary-contrast-text: var(--secondary-main);
+ --ak-chip-color-semi-filled-outlined-primary-contrast-text: var(
+ --primary-dark
+ );
+ --ak-chip-color-semi-filled-outlined-secondary-background: var(
+ --neutral-grey-200
+ );
+ --ak-chip-color-semi-filled-outlined-secondary-contrast-text: var(
+ --secondary-main
+ );
--ak-chip-color-semi-filled-outlined-success-background: var(--success-light);
- --ak-chip-color-semi-filled-outlined-success-contrast-text: var(--success-main);
+ --ak-chip-color-semi-filled-outlined-success-contrast-text: var(
+ --success-main
+ );
--ak-chip-color-semi-filled-outlined-error-background: var(--error-light);
--ak-chip-color-semi-filled-outlined-error-contrast-text: var(--error-main);
--ak-chip-color-semi-filled-outlined-warn-background: var(--warn-light);
@@ -512,7 +524,8 @@ body {
--github-account-border-color: var(--border-color-1);
// variables for organization-name-header
- --organization-name-header-border-color: var(--neutral-grey-100);
+ --organization-name-header-background-color: var(--background-main);
+ --organization-name-header-border-color: var(--border-color-1);
--organization-name-header-border-radius: var(--border-radius);
--organization-name-color-primary-light: var(--primary-light);
--organization-name-color-text-disabled: var(--text-disabled);
diff --git a/app/templates/authenticated/dashboard/organization-settings.hbs b/app/templates/authenticated/dashboard/organization-settings.hbs
index b2ba986aa0..8bdde51f9b 100644
--- a/app/templates/authenticated/dashboard/organization-settings.hbs
+++ b/app/templates/authenticated/dashboard/organization-settings.hbs
@@ -1,3 +1,5 @@
{{page-title 'Organization Settings'}}
-
\ No newline at end of file
+
+ {{outlet}}
+
\ No newline at end of file
diff --git a/app/templates/authenticated/dashboard/organization-settings/index.hbs b/app/templates/authenticated/dashboard/organization-settings/index.hbs
new file mode 100644
index 0000000000..81fe8b94d3
--- /dev/null
+++ b/app/templates/authenticated/dashboard/organization-settings/index.hbs
@@ -0,0 +1,3 @@
+{{page-title 'Settings'}}
+
+
\ No newline at end of file
diff --git a/app/templates/authenticated/dashboard/organization-settings/service-account.hbs b/app/templates/authenticated/dashboard/organization-settings/service-account.hbs
new file mode 100644
index 0000000000..fedcaf47c7
--- /dev/null
+++ b/app/templates/authenticated/dashboard/organization-settings/service-account.hbs
@@ -0,0 +1,3 @@
+{{page-title 'Service Account'}}
+
+
\ No newline at end of file
diff --git a/tests/unit/adapters/service-account-test.js b/tests/unit/adapters/service-account-test.js
new file mode 100644
index 0000000000..ac5a824cdb
--- /dev/null
+++ b/tests/unit/adapters/service-account-test.js
@@ -0,0 +1,13 @@
+import { module, test } from 'qunit';
+
+import { setupTest } from 'irene/tests/helpers';
+
+module('Unit | Adapter | service account', function (hooks) {
+ setupTest(hooks);
+
+ // Replace this with your real tests.
+ test('it exists', function (assert) {
+ let adapter = this.owner.lookup('adapter:service-account');
+ assert.ok(adapter);
+ });
+});
diff --git a/tests/unit/controllers/authenticated/dashboard/organization-settings/service-account-test.js b/tests/unit/controllers/authenticated/dashboard/organization-settings/service-account-test.js
new file mode 100644
index 0000000000..847e45a62f
--- /dev/null
+++ b/tests/unit/controllers/authenticated/dashboard/organization-settings/service-account-test.js
@@ -0,0 +1,17 @@
+import { module, test } from 'qunit';
+import { setupTest } from 'irene/tests/helpers';
+
+module(
+ 'Unit | Controller | authenticated/dashboard/organization-settings/service-account',
+ function (hooks) {
+ setupTest(hooks);
+
+ // TODO: Replace this with your real tests.
+ test('it exists', function (assert) {
+ let controller = this.owner.lookup(
+ 'controller:authenticated/dashboard/organization-settings/service-account'
+ );
+ assert.ok(controller);
+ });
+ }
+);
diff --git a/tests/unit/models/service-account-test.js b/tests/unit/models/service-account-test.js
new file mode 100644
index 0000000000..bc21a5c218
--- /dev/null
+++ b/tests/unit/models/service-account-test.js
@@ -0,0 +1,14 @@
+import { module, test } from 'qunit';
+
+import { setupTest } from 'irene/tests/helpers';
+
+module('Unit | Model | service account', function (hooks) {
+ setupTest(hooks);
+
+ // Replace this with your real tests.
+ test('it exists', function (assert) {
+ let store = this.owner.lookup('service:store');
+ let model = store.createRecord('service-account', {});
+ assert.ok(model);
+ });
+});