Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
78d4ff6
feat: add role based access control
bosschaert Mar 28, 2025
471f853
fix: rebase with main
ravverma Jun 3, 2025
e74d137
feat: RBAC implementation
ravverma Jun 4, 2025
5f66ebe
Merge branch 'main' into rbac
ravverma Jun 4, 2025
38d6c29
fix: merging main
ravverma Jun 10, 2025
eb4497c
fix : rebase and test fix
ravverma Jun 20, 2025
6a37e6e
fix: temp dep updated
ravverma Jun 20, 2025
3031bbe
fix: rebase with main
ravverma Jun 23, 2025
5d22554
adding api for role creation
ravverma Jun 24, 2025
24eda42
skip failing test
ravverma Jun 24, 2025
451d01b
updating dependency
ravverma Jun 25, 2025
908dadb
Merge branch 'main' into rbac
ravverma Jun 25, 2025
153d7d0
updating dependency
ravverma Jun 25, 2025
43c1253
fixing order of execution and test
ravverma Jun 27, 2025
83dd039
fixing order of execution and test
ravverma Jun 27, 2025
4479edd
fixing order of execution
ravverma Jun 27, 2025
146543e
adding log in libs
ravverma Jun 27, 2025
506dc85
reording again
ravverma Jun 27, 2025
fce7b89
reording again
ravverma Jun 27, 2025
09affc7
fix: setting chain
ravverma Jul 7, 2025
70deb22
fix: reverting order with ideal chain
ravverma Jul 7, 2025
a9ee138
dependency update
ravverma Jul 7, 2025
63a1bc7
rebase with main
ravverma Jul 7, 2025
6c8ace7
Dependency update
ravverma Jul 8, 2025
e750cab
fix: updating dependency
ravverma Jul 11, 2025
2298fd5
fix: rebase with main
ravverma Jul 11, 2025
ea764ed
fix: rebase with main
ravverma Jul 11, 2025
e814a54
updating dependency
ravverma Jul 15, 2025
3f5a73e
rebase with main
ravverma Jul 15, 2025
b14e1ef
fix: rebase with main
ravverma Jul 30, 2025
fdda236
fix: rebase with main
ravverma Aug 5, 2025
bf3d27f
fix: rebase with main
ravverma Aug 5, 2025
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
2 changes: 1 addition & 1 deletion .vscode/debug-node.sh
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,4 @@ export NVM_DIR="$HOME/.nvm"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh"

# Use the current Node.js version
exec "$(which node)" "$@"
exec "$(which node)" "$@"
19,797 changes: 9,730 additions & 10,067 deletions package-lock.json

Large diffs are not rendered by default.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,9 @@
"@adobe/helix-universal-logger": "3.0.27",
"@adobe/spacecat-shared-athena-client": "1.2.4",
"@adobe/spacecat-shared-brand-client": "1.1.19",
"@adobe/spacecat-shared-data-access": "2.43.2",
"@adobe/spacecat-shared-data-access": "https://gitpkg.vercel.app/adobe/spacecat-shared/packages/spacecat-shared-data-access?rbac",
"@adobe/spacecat-shared-gpt-client": "1.5.19",
"@adobe/spacecat-shared-http-utils": "1.15.2",
"@adobe/spacecat-shared-http-utils": "https://gitpkg.vercel.app/adobe/spacecat-shared/packages/spacecat-shared-http-utils?rbac",
"@adobe/spacecat-shared-ims-client": "1.8.7",
"@adobe/spacecat-shared-rum-api-client": "2.36.2",
"@adobe/spacecat-shared-scrape-client": "1.0.6",
Expand Down
3 changes: 1 addition & 2 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,6 @@ async function run(request, context) {
const scrapeJobController = ScrapeJobController(context);
const llmoController = LlmoController(context);
const fixesController = new FixesController(context);

/* ---------- build MCP registry & controller ---------- */
const mcpRegistry = buildRegistry({
auditsController,
Expand Down Expand Up @@ -190,10 +189,10 @@ async function run(request, context) {
const { WORKSPACE_EXTERNAL } = SLACK_TARGETS;

export const main = wrap(run)
.with(dataAccess)
.with(authWrapper, {
authHandlers: [JwtHandler, AdobeImsHandler, ScopedApiKeyHandler, LegacyApiKeyHandler],
})
.with(dataAccess)
.with(bodyData)
.with(multipartFormData)
.with(enrichPathInfo)
Expand Down
2 changes: 1 addition & 1 deletion src/utils/route-utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,7 @@ export default function matchPath(httpMethod, incomingPath, routeDefinitions) {
return { handler, params };
}

return null; // Continue reducing if no match
return matched; // Continue reducing if no match
}, null); // Initial value is null
}

Expand Down
26 changes: 26 additions & 0 deletions test/controllers/audits.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,30 @@ describe('Audits Controller', () => {
'patchAuditForSite',
];

const acls = [{
acl: [{
actions: ['C', 'R', 'U', 'D'],
path: '/organization/org-id/**',
},
{
actions: ['C', 'R', 'U', 'D'],
path: '/organization/org-id',
},
],
}];

const actCtx = {
acls,
aclEntities: {
// Right now only check site
exclude: [
'site', 'apiKey', 'audit', 'configuration', 'experiment',
'importJob', 'importUrl', 'keyEvent', 'latestAudit',
'opportunity', 'siteCandidate', 'siteTopPage', 'suggestion', 'asyncJob',
],
},
};

const mockRawAudits = [
{
siteId: SITE_ID,
Expand Down Expand Up @@ -182,6 +206,8 @@ describe('Audits Controller', () => {
name: 'Test Org',
imsOrgId: 'org-id@AdobeOrg',
});
mockOrganization.aclCtx = actCtx;
mockOrganization.log = console;
const mockSite = new Site(
{
entities: {
Expand Down
29 changes: 29 additions & 0 deletions test/controllers/brands.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ describe('Brands Controller', () => {
info: sandbox.stub(),
error: sandbox.stub(),
warn: sandbox.stub(),
debug: sandbox.stub(),
};

const ORGANIZATION_ID = '9033554c-de8a-44ac-a356-09b51af8cc28';
Expand Down Expand Up @@ -83,6 +84,32 @@ describe('Brands Controller', () => {
loggerStub,
));

const acls = [{
acl: [{
actions: ['C', 'R', 'U', 'D'],
path: '/organization/9033554c-de8a-44ac-a356-09b51af8cc28/**',
},
{
actions: ['C', 'R', 'U', 'D'],
path: '/organization/9033554c-de8a-44ac-a356-09b51af8cc28',
},
],
}];

const actCtx = {
acls,
aclEntities: {
// Right now only check site
exclude: [
'site', 'apiKey', 'audit', 'configuration', 'experiment',
'importJob', 'importUrl', 'keyEvent', 'latestAudit',
'opportunity', 'siteCandidate', 'siteTopPage', 'suggestion', 'asyncJob',
],
},
};

organizations[0].aclCtx = actCtx;

const getCollectionStub = stub();
getCollectionStub.returns({
schema: OrganizationSchema,
Expand Down Expand Up @@ -122,6 +149,8 @@ describe('Brands Controller', () => {
loggerStub,
));

sites[0].aclCtx = actCtx;

let mockDataAccess;
let brandsController;
let mockEnv;
Expand Down
4 changes: 2 additions & 2 deletions test/controllers/fixes.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ describe('Fixes Controller', () => {
const siteId = '86ef4aae-7296-417d-9658-8cd4c7edc374';
const opportunityId = 'a3d2f1e9-5f4c-4e6b-8c7d-0c7b5a2f1a2f';

const log = { ...console, debug: () => undefined, info: () => undefined };
const log = { ...console, debug: () => sandbox.stub(), info: () => sandbox.stub() };

beforeEach(() => {
const { fixEntity, opportunity, suggestion } = electroService.entities;
Expand All @@ -75,7 +75,7 @@ describe('Fixes Controller', () => {
.withArgs({ opportunityId })
.callsFake((data) => ({ go: async () => ({ data: { ...data, siteId } }) }));

const entityRegistry = new EntityRegistry(electroService, log);
const entityRegistry = new EntityRegistry(electroService, {}, log);
const dataAccess = entityRegistry.getCollections();
fixEntityCollection = dataAccess.FixEntity;
suggestionCollection = dataAccess.Suggestion;
Expand Down
52 changes: 32 additions & 20 deletions test/controllers/import.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,29 +27,41 @@ import { ErrorWithStatusCode } from '../../src/support/utils.js';
use(sinonChai);
use(chaiAsPromised);

const createImportJob = (data) => (new ImportJob(
{
entities: {
importJob: {
model: {
schema: { attributes: { status: { type: 'string', get: (value) => value } } },
const createImportJob = (data) => {
const job = new ImportJob(
{
entities: {
importJob: {
model: {
schema: { attributes: { status: { type: 'string', get: (value) => value } } },
},
patch: sinon.stub().returns({ go: () => {}, set: () => {} }),
remove: sinon.stub().returns({ go: () => {} }),
},
patch: sinon.stub().returns({ go: () => {}, set: () => {} }),
remove: sinon.stub().returns({ go: () => {} }),
},
},
},
{
log: console,
getCollection: stub().returns({
schema: ImportJobSchema,
findById: stub(),
}),
},
ImportJobSchema,
data,
console,
));
{
log: console,
getCollection: stub().returns({
schema: ImportJobSchema,
findById: stub(),
}),
},
ImportJobSchema,
data,
console,
);
job.aclCtx = {
acls: [],
aclEntities: {
exclude: [
'importJob',
'importUrl',
],
},
};
return job;
};

const createImportUrl = (data) => (new ImportUrl(
{ entities: { importUrl: {} } },
Expand Down
70 changes: 70 additions & 0 deletions test/controllers/organizations.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,73 @@ use(sinonChai);
describe('Organizations Controller', () => {
const sandbox = sinon.createSandbox();
const orgId = '9033554c-de8a-44ac-a356-09b51af8cc28';

const acls1 = [{
acl: [{
actions: ['C', 'R', 'U', 'D'],
path: '/organization/9033554c-de8a-44ac-a356-09b51af8cc28/**',
},
{
actions: ['C', 'R', 'U', 'D'],
path: '/organization/9033554c-de8a-44ac-a356-09b51af8cc28',
},
],
}];
const acls2 = [{
acl: [{
actions: ['C', 'R', 'U', 'D'],
path: '/organization/5f3b3626-029c-476e-924b-0c1bba2e871f/**',
},
{
actions: ['C', 'R', 'U', 'D'],
path: '/organization/5f3b3626-029c-476e-924b-0c1bba2e871f',
},
],
}];
const acls3 = [{
acl: [{
actions: ['C', 'R', 'U', 'D'],
path: '/organization/org3/**',
},
{
actions: ['C', 'R', 'U', 'D'],
path: '/organization/org3',
},
],
}];

const actCtx1 = {
acls: acls1,
aclEntities: {
// Right now only check site
exclude: [
'site', 'apiKey', 'audit', 'configuration', 'experiment',
'importJob', 'importUrl', 'keyEvent', 'latestAudit',
'opportunity', 'siteCandidate', 'siteTopPage', 'suggestion', 'asyncJob',
],
},
};
const actCtx2 = {
acls: acls2,
aclEntities: {
exclude: [
'site', 'apiKey', 'audit', 'configuration', 'experiment',
'importJob', 'importUrl', 'keyEvent', 'latestAudit',
'opportunity', 'siteCandidate', 'siteTopPage', 'suggestion', 'asyncJob',
],
},
};
const actCtx3 = {
acls: acls3,
aclEntities: {
exclude: [
'site', 'apiKey', 'audit', 'configuration', 'experiment',
'importJob', 'importUrl', 'keyEvent', 'latestAudit',
'opportunity', 'siteCandidate', 'siteTopPage', 'suggestion', 'asyncJob',
],
},
};

const sites = [
{
siteId: 'site1',
Expand Down Expand Up @@ -127,6 +194,9 @@ describe('Organizations Controller', () => {
organizations[0].getConfig = sinon.stub().returns(sampleConfig1);
organizations[1].getConfig = sinon.stub().returns(sampleConfig1);
organizations[2].getConfig = sinon.stub().returns(sampleConfig2);
organizations[0].aclCtx = actCtx1;
organizations[1].aclCtx = actCtx2;
organizations[2].aclCtx = actCtx3;

const organizationFunctions = [
'createOrganization',
Expand Down
25 changes: 19 additions & 6 deletions test/controllers/scrape-job.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,16 @@ import { ErrorWithStatusCode } from '../../src/support/utils.js';
use(sinonChai);
use(chaiAsPromised);

const actCtx = {
acls: [],
aclEntities: {
// Right now only check site
exclude: [
'scrapeJob', 'scrapeUrl',
],
},
};

const createScrapeJob = (data) => (new ScrapeJob(
{
entities: {
Expand All @@ -41,6 +51,7 @@ const createScrapeJob = (data) => (new ScrapeJob(
},
{
log: console,
aclCtx: actCtx,
getCollection: stub().returns({
schema: ScrapeJobSchema,
findById: stub(),
Expand Down Expand Up @@ -148,7 +159,9 @@ describe('ScrapeJobController tests', () => {
if (jobId !== exampleJob.scrapeJobId) {
throw new ErrorWithStatusCode('Not found', 404);
}
return createScrapeJob(exampleJob);
const job = createScrapeJob(exampleJob);
job.aclCtx = actCtx;
return job;
});

scrapeJobConfiguration = {
Expand Down Expand Up @@ -306,11 +319,8 @@ describe('ScrapeJobController tests', () => {
});

it('should pick another scrape queue when the first one is in use', async () => {
baseContext.dataAccess.getScrapeJobsByStatus = sandbox.stub().resolves([
createScrapeJob({
...exampleJob,
}),
]);
const job = createScrapeJob(exampleJob);
baseContext.dataAccess.getScrapeJobsByStatus = sandbox.stub().resolves([job]);
const response = await scrapeJobController.createScrapeJob(baseContext);
expect(response).to.be.an.instanceOf(Response);
expect(response.status).to.equal(202);
Expand Down Expand Up @@ -567,6 +577,7 @@ describe('ScrapeJobController tests', () => {

it('should return an array of scrape jobs', async () => {
const job = createScrapeJob(exampleJob);
job.aclCtx = actCtx;
baseContext.dataAccess.ScrapeJob.allByDateRange = sandbox.stub().resolves([job]);
baseContext.params.startDate = '2022-10-05T14:48:00.000Z';
baseContext.params.endDate = '2022-10-07T14:48:00.000Z';
Expand Down Expand Up @@ -603,6 +614,7 @@ describe('ScrapeJobController tests', () => {
describe('getScrapeJobsByBaseURL', () => {
it('should return an array of scrape jobs', async () => {
const job = createScrapeJob(exampleJob);
job.aclCtx = actCtx;
baseContext.dataAccess.ScrapeJob.allByBaseURL = sandbox.stub().resolves([job]);
baseContext.params.baseURL = 'aHR0cHM6Ly93d3cuZXhhbXBsZS5jb20=';

Expand All @@ -615,6 +627,7 @@ describe('ScrapeJobController tests', () => {

it('should return an array of scrape jobs for baseUrl and processingType', async () => {
const job = createScrapeJob(exampleJob);
job.aclCtx = actCtx;
baseContext.dataAccess.ScrapeJob.allByBaseURLAndProcessingType = sandbox
.stub()
.resolves([job]);
Expand Down
Loading