Skip to content

Commit

Permalink
chore: add template schema validation github action (#4)
Browse files Browse the repository at this point in the history
  • Loading branch information
johneke-auth0 authored Jan 26, 2024
1 parent d5ff145 commit 74d76f3
Show file tree
Hide file tree
Showing 6 changed files with 371 additions and 0 deletions.
82 changes: 82 additions & 0 deletions .github/workflows/validators.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
name: Templates Validation

on:
pull_request:
types:
- opened
- synchronize

jobs:
install-cache:
runs-on: ubuntu-latest
name: Install & Cache modules
steps:
- uses: actions/checkout@v3
- name: Cache node modules
uses: actions/cache@v3
env:
cache-name: cache-node-modules
with:
path: |
node_modules
key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- uses: actions/setup-node@v3
with:
node-version: '18.x'
always-auth: true
- name: Install dependencies
if: steps.cache-dependencies.outputs.cache-hit != true
run: npm ci

lint:
needs: install-cache
name: Lint
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
with:
fetch-depth: 0
- name: Use node.js
uses: actions/setup-node@v3
with:
node-version: '18'
- name: Restore Cached Dependencies
uses: actions/cache@v3
id: cache-dependencies
with:
path: |
node_modules
key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Lint Repo
run: npm run pretty:check:templates

validate:
needs: lint
name: Validate Schema
runs-on: ubuntu-latest

steps:
- name: Check Out Repository
uses: actions/checkout@v4

- name: Set Up Node.js
uses: actions/setup-node@v3
with:
node-version: 18

- name: Restore Cached Dependencies
uses: actions/cache@v3
id: cache-dependencies
with:
path: |
node_modules
key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }}
restore-keys: |
${{ runner.os }}-npm-
- name: Validate Templates
run: npm run validate:templates
73 changes: 73 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

25 changes: 25 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
{
"name": "opensource-marketplace",
"version": "0.0.1",
"description": "A collection 3rd-party code that's used to manage the open source marketplace contributions.",
"scripts": {
"pretty:check:templates": "prettier --check ./templates",
"validate:templates": "node ./scripts/templates/validate-templates.js"
},
"repository": {
"type": "git",
"url": "git+https://github.com/auth0/opensource-marketplace.git"
},
"author": "",
"license": "Apache-2.0",
"bugs": {
"url": "https://github.com/auth0/opensource-marketplace/issues"
},
"homepage": "https://github.com/auth0/opensource-marketplace#readme",
"devDependencies": {
"js-yaml": "^4.1.0",
"prettier": "^3.0.3",
"zod": "^3.22.4",
"zod-validation-error": "^2.1.0"
}
}
140 changes: 140 additions & 0 deletions scripts/templates/validate-templates.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,140 @@
const yamlParser = require('js-yaml');
const fs = require('fs');
const path = require('path');
const { z } = require('zod');
const { fromZodError } = require('zod-validation-error');

// Changes here must be reflected here:
// https://github.com/auth0/managed-marketplace/blob/main/prisma/schema.prisma#L198
const IntegrationTrigger = [
'POST_LOGIN',
'CREDENTIALS_EXCHANGE',
'PRE_USER_REGISTRATION',
'POST_USER_REGISTRATION',
'POST_CHANGE_PASSWORD',
'SEND_PHONE_MESSAGE',
'IGA_APPROVAL',
'IGA_CERTIFICATION',
'IGA_FULFILLMENT_ASSIGNMENT',
'IGA_FULFILLMENT_EXECUTION',
'PASSWORD_RESET_POST_CHALLENGE',
];

const UseCase = [
'MULTIFACTOR',
'ACTION_FEATURE',
'ENRICH_PROFILE',
'ACCESS_CONTROL',
];

const configValue = z.object({
label: z.string().min(2),
defaultValue: z.string().min(2),
});
const moduleValue = z.object({
name: z.string().min(2),
version: z.string().min(2),
});

const TemplateSchema = z
.object({
id: z.string().uuid(),
name: z.string().min(3),
triggers: z.array(z.enum(IntegrationTrigger)),
useCases: z.array(z.enum(UseCase)),
public: z.boolean().optional(),
published: z.boolean().optional(),
deleted: z.boolean().optional(),
description: z.string().min(3),
version: z.string().optional(),
runtime: z.string().optional(),
secrets: z.array(configValue).optional(),
config: z.array(configValue).optional(),
sourceUrl: z.string().url(),
code: z.string().min(3),
modules: z.array(moduleValue).optional(),
notes: z.string().optional(),
})
.strict();

function templateDirs() {
const dir = path.normalize(`${__dirname}/../../templates`);

// Read the contents of the directory
const items = fs.readdirSync(dir);

// Filter out only directories (folders)
return items
.map((item) => `${dir}/${item}`)
.filter((item) => {
return fs.statSync(item).isDirectory();
});
}

const templateToJSON = async (templateDir) => {
const yaml = yamlParser.load(
fs.readFileSync(`${templateDir}/manifest.yaml`, 'utf8')
);

const code = fs.readFileSync(`${templateDir}/code.js`, 'utf8');

return {
...yaml,
code,
};
};

const validateTemplates = async () => {
console.log('\n🗄️ Validating schema for all templates.\n');
try {
// GET ALL TEMPLATES
let templates;
try {
templates = templateDirs();
} catch (e) {
throw {
detail: 'failed to load templates',
err: e,
};
}

// BUNDLE INTO JSON
for (const templatePath of templates) {
console.log('🗄️ Validating template:', templatePath);

let template;
try {
template = await templateToJSON(templatePath);
} catch (e) {
throw {
detail: `failed to load template: ${templatePath}`,
err: e,
};
}

// VALIDATE JSON
try {
TemplateSchema.parse(template);
} catch (e) {
throw {
detail: `template validation failed, template: ${templatePath}`,
err: fromZodError(e),
};
}
}
} catch (e) {
console.error(
'⛔️ - There was an error validatong templates:',
e.detail,
'\n\n',
e.err
);
process.exit(1);
}

console.log('\n✅ Validation complete.\n');
};

(async function () {
await validateTemplates();
})();
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* Handler that will be called during the execution of a Password Reset / Post Challenge Flow.
*
* --- AUTH0 ACTIONS TEMPLATE https://github.com/auth0/os-marketplace/blob/main/templates/active-directory-groups-PASSWORD_RESET_POST_CHALLENGE ---
*
* @param {Event} event - Details about the post challenge request.
* @param {PasswordResetPostChallengeAPI} api - Interface whose methods can be used to change the behavior of the post challenge flow.
*/
exports.onExecutePostChallenge = async (event, api) => {
// ensure that the allowed group is configured
const groupAllowed = event.secrets.ALLOWED_GROUP;
if (!groupAllowed) {
return api.access.deny('Invalid configuration');
}

// get the users groups
let groups = event.user.groups || [];
if (!Array.isArray(groups)) {
groups = [groups];
}

// if the allowed group is not one of the users, deny access
if (!groups.includes(groupAllowed)) {
return api.access.deny('Access denied');
}
};

/**
* Handler that will be invoked when this action is resuming after an external redirect. If your
* onExecutePostChallenge function does not perform a redirect, this function can be safely ignored.
*
* @param {Event} event - Details about the user and the context in which they are logging in.
* @param {PasswordResetPostChallengeAPI} api - Interface whose methods can be used to change the behavior of the post challenge flow.
*/
// exports.onContinuePostChallenge = async (event, api) => {
// };
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
id: 'c9fa1544-b8bd-4211-950e-3c6ba2a946f4'
name: 'Check if a user belongs to an active directory group.'
description: 'Check if a user belongs to an AD group and if not, deny access.'
public: true
triggers:
- 'PASSWORD_RESET_POST_CHALLENGE'
runtime: 'node18'
modules: []
sourceUrl: 'https://github.com/auth0/os-marketplace/blob/main/templates/active-directory-groups-PASSWORD_RESET_POST_CHALLENGE'
notes: |
**Secrets**
* `ALLOWED_GROUP` - the name of the allowed group.
useCases:
- 'ACCESS_CONTROL'

0 comments on commit 74d76f3

Please sign in to comment.