From f9e85aa9aa8f54e6eae10f79a0921d7001b5b2c3 Mon Sep 17 00:00:00 2001 From: Cameron Perera Date: Mon, 3 Jun 2024 15:27:04 -0500 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=8E=B8=20add=20grant=5Fscope=5Fid?= =?UTF-8?q?s=20to=20role=20model=20(#2308)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 🎸 add grant_scope_ids to role model ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12709 * refactor: 💡 remove removeGrantScopes method ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12709 * style: 💄 add braces around if statement ✅ Closes: https://hashicorp.atlassian.net/browse/ICU-12709 --- addons/api/addon/generated/models/role.js | 6 +++ addons/api/addon/models/role.js | 14 +++++++ addons/api/addon/serializers/role.js | 37 ++++++++++++++--- addons/api/tests/unit/models/role-test.js | 31 ++++++++++++++ .../api/tests/unit/serializers/role-test.js | 41 +++++++++++++++++++ 5 files changed, 123 insertions(+), 6 deletions(-) diff --git a/addons/api/addon/generated/models/role.js b/addons/api/addon/generated/models/role.js index 5241d9e6a..db9f0bd9d 100644 --- a/addons/api/addon/generated/models/role.js +++ b/addons/api/addon/generated/models/role.js @@ -28,6 +28,12 @@ export default class GeneratedRoleModel extends BaseModel { }) grant_scope_id; + @attr('array', { + description: + "The scopes to which this role applies. Defaults to the role's scope.", + }) + grant_scope_ids; + @attr('date', { description: 'The time this resource was created\nOutput only.', readOnly: true, diff --git a/addons/api/addon/models/role.js b/addons/api/addon/models/role.js index 13be5158a..f5b6f1326 100644 --- a/addons/api/addon/models/role.js +++ b/addons/api/addon/models/role.js @@ -74,6 +74,20 @@ export default class RoleModel extends GeneratedRoleModel { // =methods + /** + * Saves grant scope ids on the role via the `set-grant-scopes` method. + * @param {[string]} grantScopeIDs + * @return {Promise} + */ + setGrantScopes(grantScopeIDs) { + return this.save({ + adapterOptions: { + method: 'set-grant-scopes', + grantScopeIDs, + }, + }); + } + /** * Saves grant strings on the role via the `set-grants` method. * See serializer and adapter for more information. diff --git a/addons/api/addon/serializers/role.js b/addons/api/addon/serializers/role.js index 29b13ac45..693cf6b8a 100644 --- a/addons/api/addon/serializers/role.js +++ b/addons/api/addon/serializers/role.js @@ -15,23 +15,35 @@ export default class RoleSerializer extends ApplicationSerializer { * serialized. * If `adapterOptions.principalIDs` is set (to an array of user and * group IDs), then the payload is serialized via `serializeWithPrincipals`. + * If `adapterOptions.grantScopeIDs` is set (to an array of scope IDs or + * keywords), then the payload is serialized via `serializeWithGrantScopeIDs`. * @override * @param {Snapshot} snapshot * @return {object} */ serialize(snapshot) { - const grantStrings = get(snapshot, 'adapterOptions.grantStrings'); let serialized = super.serialize(...arguments); - if (grantStrings) + + const grantStrings = get(snapshot, 'adapterOptions.grantStrings'); + if (grantStrings) { serialized = this.serializeWithGrantStrings(snapshot, grantStrings); + } + const principalIDs = snapshot?.adapterOptions?.principalIDs; - if (principalIDs) + if (principalIDs) { serialized = this.serializeWithPrincipals(snapshot, principalIDs); + } + + const grantScopeIDs = snapshot?.adapterOptions?.grantScopeIDs; + if (grantScopeIDs) { + serialized = this.serializeWithGrantScopeIDs(snapshot, grantScopeIDs); + } + return serialized; } /** - * Returns a payload containing only the grants array. + * Returns a payload containing only the grants array and version. * @param {Snapshot} snapshot * @param {[string]} grantStrings * @return {object} @@ -44,8 +56,8 @@ export default class RoleSerializer extends ApplicationSerializer { } /** - * Returns a payload containing only the principal_ids array using IDs - * passed into the function (rather than existing principals on the model). + * Returns a payload containing only the principal_ids array using IDs passed + * into the function (rather than existing principals on the model) and version. * @param {Snapshot} snapshot * @param {[string]} principalIDs * @return {object} @@ -56,4 +68,17 @@ export default class RoleSerializer extends ApplicationSerializer { principal_ids: principalIDs, }; } + + /** + * Returns a payload containing only the grant_scope_ids array and version. + * @param {Snapshot} snapshot + * @param {[string]} grantScopeIDs + * @return {object} + */ + serializeWithGrantScopeIDs(snapshot, grantScopeIDs) { + return { + version: snapshot.attr('version'), + grant_scope_ids: grantScopeIDs, + }; + } } diff --git a/addons/api/tests/unit/models/role-test.js b/addons/api/tests/unit/models/role-test.js index 12567ac92..23f4bbb53 100644 --- a/addons/api/tests/unit/models/role-test.js +++ b/addons/api/tests/unit/models/role-test.js @@ -17,6 +17,37 @@ module('Unit | Model | role', function (hooks) { assert.ok(model); }); + test('it has a `setGrantScopes` method that targets a specific POST API endpoint and serialization', async function (assert) { + assert.expect(1); + this.server.post('/roles/r_123:set-grant-scopes', (schema, request) => { + const body = JSON.parse(request.requestBody); + assert.deepEqual(body, { + grant_scope_ids: ['this', 'children'], + version: 1, + }); + return { id: 'r_123' }; + }); + const store = this.owner.lookup('service:store'); + store.push({ + data: { + id: 'r_123', + type: 'role', + attributes: { + name: 'Role', + description: 'Description', + grant_scope_ids: ['this'], + version: 1, + scope: { + scope_id: 'o_1', + type: 'scope', + }, + }, + }, + }); + const model = store.peekRecord('role', 'r_123'); + await model.setGrantScopes(['this', 'children']); + }); + test('it has a `saveGrantStrings` method that targets a specific POST API endpoint and serialization', async function (assert) { assert.expect(1); this.server.post('/roles/123abc:set-grants', (schema, request) => { diff --git a/addons/api/tests/unit/serializers/role-test.js b/addons/api/tests/unit/serializers/role-test.js index f212bbc09..abf7d56cd 100644 --- a/addons/api/tests/unit/serializers/role-test.js +++ b/addons/api/tests/unit/serializers/role-test.js @@ -26,6 +26,7 @@ module('Unit | Serializer | role', function (hooks) { description: 'Description', version: 1, grant_scope_id: null, + grant_scope_ids: [], }); }); @@ -49,6 +50,46 @@ module('Unit | Serializer | role', function (hooks) { }); }); + test('it serializes only principals when `adapterOptions.principalIDs` is set', function (assert) { + const store = this.owner.lookup('service:store'); + const serializer = store.serializerFor('role'); + const record = store.createRecord('role', { + name: 'User', + description: 'Description', + principal_ids: [], + version: 1, + }); + const snapshot = record._createSnapshot(); + snapshot.adapterOptions = { + principalIDs: ['u_123'], + }; + const serializedRecord = serializer.serialize(snapshot); + assert.deepEqual(serializedRecord, { + principal_ids: ['u_123'], + version: 1, + }); + }); + + test('it serializes only grant scope IDs when `adapterOptions.grantScopeIDs` is set', function (assert) { + const store = this.owner.lookup('service:store'); + const serializer = store.serializerFor('role'); + const record = store.createRecord('role', { + name: 'User', + description: 'Description', + grant_scope_ids: ['this'], + version: 1, + }); + const snapshot = record._createSnapshot(); + snapshot.adapterOptions = { + grantScopeIDs: ['this', 'children'], + }; + const serializedRecord = serializer.serialize(snapshot); + assert.deepEqual(serializedRecord, { + grant_scope_ids: ['this', 'children'], + version: 1, + }); + }); + test('it normalizes records with array fields', function (assert) { const store = this.owner.lookup('service:store'); const serializer = store.serializerFor('role');