mirror of
https://github.com/arthur-pbty/portfolio2023.git
synced 2026-06-03 23:36:21 +02:00
339 lines
11 KiB
JavaScript
339 lines
11 KiB
JavaScript
"use strict";
|
|
/*
|
|
* @adonisjs/auth
|
|
*
|
|
* (c) Harminder Virk <virk@adonisjs.com>
|
|
*
|
|
* For the full copyright and license information, please view the LICENSE
|
|
* file that was distributed with this source code.
|
|
*/
|
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
const path_1 = require("path");
|
|
const helpers_1 = require("@poppinss/utils/build/helpers");
|
|
// const USER_MIGRATION_TIME_PREFIX = '1587988332388'
|
|
// const TOKENS_MIGRATION_TIME_PREFIX = '1592489784670'
|
|
/**
|
|
* Base path to contract stub partials
|
|
*/
|
|
const CONTRACTS_PARTIALS_BASE = './contract/partials';
|
|
/**
|
|
* Base path to config stub partials
|
|
*/
|
|
const CONFIG_PARTIALS_BASE = './config/partials';
|
|
/**
|
|
* Prompt choices for the provider selection
|
|
*/
|
|
const PROVIDER_PROMPT_CHOICES = [
|
|
{
|
|
name: 'lucid',
|
|
message: 'Lucid',
|
|
hint: ' (Uses Data Models)',
|
|
},
|
|
{
|
|
name: 'database',
|
|
message: 'Database',
|
|
hint: ' (Uses Database QueryBuilder)',
|
|
},
|
|
];
|
|
/**
|
|
* Prompt choices for the guard selection
|
|
*/
|
|
const GUARD_PROMPT_CHOICES = [
|
|
{
|
|
name: 'web',
|
|
message: 'Web',
|
|
hint: ' (Uses sessions for managing auth state)',
|
|
},
|
|
{
|
|
name: 'api',
|
|
message: 'API tokens',
|
|
hint: ' (Uses database backed opaque tokens)',
|
|
},
|
|
{
|
|
name: 'basic',
|
|
message: 'Basic Auth',
|
|
hint: ' (Uses HTTP Basic auth for authenticating requests)',
|
|
},
|
|
];
|
|
/**
|
|
* Prompt choices for the tokens provider selection
|
|
*/
|
|
const TOKENS_PROVIDER_PROMPT_CHOICES = [
|
|
{
|
|
name: 'database',
|
|
message: 'Database',
|
|
hint: ' (Uses SQL table for storing API tokens)',
|
|
},
|
|
{
|
|
name: 'redis',
|
|
message: 'Redis',
|
|
hint: ' (Uses Redis for storing API tokens)',
|
|
},
|
|
];
|
|
/**
|
|
* Returns absolute path to the stub relative from the templates
|
|
* directory
|
|
*/
|
|
function getStub(...relativePaths) {
|
|
return (0, path_1.join)(__dirname, 'templates', ...relativePaths);
|
|
}
|
|
/**
|
|
* Creates the model file
|
|
*/
|
|
function makeModel(projectRoot, app, sink, state) {
|
|
const modelsDirectory = app.resolveNamespaceDirectory('models') || 'app/Models';
|
|
const modelPath = (0, path_1.join)(modelsDirectory, `${state.modelName}.ts`);
|
|
const template = new sink.files.MustacheFile(projectRoot, modelPath, getStub('model.txt'));
|
|
if (template.exists()) {
|
|
sink.logger.action('create').skipped(`${modelPath} file already exists`);
|
|
return;
|
|
}
|
|
template.apply(state).commit();
|
|
sink.logger.action('create').succeeded(modelPath);
|
|
}
|
|
/**
|
|
* Create the migration file
|
|
*/
|
|
function makeUsersMigration(projectRoot, app, sink, state) {
|
|
const migrationsDirectory = app.directoriesMap.get('migrations') || 'database';
|
|
const migrationPath = (0, path_1.join)(migrationsDirectory, `${Date.now()}_${state.usersTableName}.ts`);
|
|
const template = new sink.files.MustacheFile(projectRoot, migrationPath, getStub('migrations/auth.txt'));
|
|
if (template.exists()) {
|
|
sink.logger.action('create').skipped(`${migrationPath} file already exists`);
|
|
return;
|
|
}
|
|
template.apply(state).commit();
|
|
sink.logger.action('create').succeeded(migrationPath);
|
|
}
|
|
/**
|
|
* Create the migration file
|
|
*/
|
|
function makeTokensMigration(projectRoot, app, sink, state) {
|
|
const migrationsDirectory = app.directoriesMap.get('migrations') || 'database';
|
|
const migrationPath = (0, path_1.join)(migrationsDirectory, `${Date.now()}_${state.tokensTableName}.ts`);
|
|
const template = new sink.files.MustacheFile(projectRoot, migrationPath, getStub('migrations/api_tokens.txt'));
|
|
if (template.exists()) {
|
|
sink.logger.action('create').skipped(`${migrationPath} file already exists`);
|
|
return;
|
|
}
|
|
template.apply(state).commit();
|
|
sink.logger.action('create').succeeded(migrationPath);
|
|
}
|
|
/**
|
|
* Create the middleware(s)
|
|
*/
|
|
function makeMiddleware(projectRoot, app, sink, state) {
|
|
const middlewareDirectory = app.resolveNamespaceDirectory('middleware') || 'app/Middleware';
|
|
/**
|
|
* Auth middleware
|
|
*/
|
|
const authPath = (0, path_1.join)(middlewareDirectory, 'Auth.ts');
|
|
const authTemplate = new sink.files.MustacheFile(projectRoot, authPath, getStub('middleware/Auth.txt'));
|
|
if (authTemplate.exists()) {
|
|
sink.logger.action('create').skipped(`${authPath} file already exists`);
|
|
}
|
|
else {
|
|
authTemplate.apply(state).commit();
|
|
sink.logger.action('create').succeeded(authPath);
|
|
}
|
|
/**
|
|
* Silent auth middleware
|
|
*/
|
|
const silentAuthPath = (0, path_1.join)(middlewareDirectory, 'SilentAuth.ts');
|
|
const silentAuthTemplate = new sink.files.MustacheFile(projectRoot, silentAuthPath, getStub('middleware/SilentAuth.txt'));
|
|
if (silentAuthTemplate.exists()) {
|
|
sink.logger.action('create').skipped(`${silentAuthPath} file already exists`);
|
|
}
|
|
else {
|
|
silentAuthTemplate.apply(state).commit();
|
|
sink.logger.action('create').succeeded(silentAuthPath);
|
|
}
|
|
}
|
|
/**
|
|
* Creates the contract file
|
|
*/
|
|
function makeContract(projectRoot, app, sink, state) {
|
|
const contractsDirectory = app.directoriesMap.get('contracts') || 'contracts';
|
|
const contractPath = (0, path_1.join)(contractsDirectory, 'auth.ts');
|
|
const template = new sink.files.MustacheFile(projectRoot, contractPath, getStub('contract/auth.txt'));
|
|
template.overwrite = true;
|
|
const partials = {
|
|
provider: getStub(CONTRACTS_PARTIALS_BASE, `user-provider-${state.provider}.txt`),
|
|
};
|
|
state.guards.forEach((guard) => {
|
|
partials[`${guard}_guard`] = getStub(CONTRACTS_PARTIALS_BASE, `${guard}-guard.txt`);
|
|
});
|
|
template.apply(state).partials(partials).commit();
|
|
sink.logger.action('create').succeeded(contractPath);
|
|
}
|
|
/**
|
|
* Makes the auth config file
|
|
*/
|
|
function makeConfig(projectRoot, app, sink, state) {
|
|
const configDirectory = app.directoriesMap.get('config') || 'config';
|
|
const configPath = (0, path_1.join)(configDirectory, 'auth.ts');
|
|
const template = new sink.files.MustacheFile(projectRoot, configPath, getStub('config/auth.txt'));
|
|
template.overwrite = true;
|
|
const partials = {
|
|
provider: getStub(CONFIG_PARTIALS_BASE, `user-provider-${state.provider}.txt`),
|
|
token_provider: getStub(CONFIG_PARTIALS_BASE, `tokens-provider-${state.tokensProvider}.txt`),
|
|
};
|
|
state.guards.forEach((guard) => {
|
|
partials[`${guard}_guard`] = getStub(CONFIG_PARTIALS_BASE, `${guard}-guard.txt`);
|
|
});
|
|
template.apply(state).partials(partials).commit();
|
|
sink.logger.action('create').succeeded(configPath);
|
|
}
|
|
/**
|
|
* Prompts user to select the provider
|
|
*/
|
|
async function getProvider(sink) {
|
|
return sink.getPrompt().choice('Select provider for finding users', PROVIDER_PROMPT_CHOICES, {
|
|
validate(choice) {
|
|
return choice && choice.length ? true : 'Select the provider for finding users';
|
|
},
|
|
});
|
|
}
|
|
/**
|
|
* Prompts user to select the tokens provider
|
|
*/
|
|
async function getTokensProvider(sink) {
|
|
return sink
|
|
.getPrompt()
|
|
.choice('Select the provider for storing API tokens', TOKENS_PROVIDER_PROMPT_CHOICES, {
|
|
validate(choice) {
|
|
return choice && choice.length ? true : 'Select the provider for storing API tokens';
|
|
},
|
|
});
|
|
}
|
|
/**
|
|
* Prompts user to select one or more guards
|
|
*/
|
|
async function getGuard(sink) {
|
|
return sink
|
|
.getPrompt()
|
|
.multiple('Select which guard you need for authentication (select using space)', GUARD_PROMPT_CHOICES, {
|
|
validate(choices) {
|
|
return choices && choices.length
|
|
? true
|
|
: 'Select one or more guards for authenticating users';
|
|
},
|
|
});
|
|
}
|
|
/**
|
|
* Prompts user for the model name
|
|
*/
|
|
async function getModelName(sink) {
|
|
return sink.getPrompt().ask('Enter model name to be used for authentication', {
|
|
validate(value) {
|
|
return !!value.trim().length;
|
|
},
|
|
});
|
|
}
|
|
/**
|
|
* Prompts user for the table name
|
|
*/
|
|
async function getTableName(sink) {
|
|
return sink.getPrompt().ask('Enter the database table name to look up users', {
|
|
validate(value) {
|
|
return !!value.trim().length;
|
|
},
|
|
});
|
|
}
|
|
/**
|
|
* Prompts user for the table name
|
|
*/
|
|
async function getMigrationConsent(sink, tableName) {
|
|
return sink
|
|
.getPrompt()
|
|
.confirm(`Create migration for the ${sink.logger.colors.underline(tableName)} table?`);
|
|
}
|
|
/**
|
|
* Instructions to be executed when setting up the package.
|
|
*/
|
|
async function instructions(projectRoot, app, sink) {
|
|
const state = {
|
|
usersTableName: '',
|
|
tokensTableName: 'api_tokens',
|
|
tokensSchemaName: 'ApiTokens',
|
|
usersSchemaName: '',
|
|
provider: 'lucid',
|
|
tokensProvider: 'database',
|
|
guards: [],
|
|
hasGuard: {
|
|
web: false,
|
|
api: false,
|
|
basic: false,
|
|
},
|
|
};
|
|
state.provider = await getProvider(sink);
|
|
state.guards = await getGuard(sink);
|
|
/**
|
|
* Need booleans for mustache templates
|
|
*/
|
|
state.guards.forEach((guard) => (state.hasGuard[guard] = true));
|
|
/**
|
|
* Make model when provider is lucid otherwise prompt for the database
|
|
* table name
|
|
*/
|
|
if (state.provider === 'lucid') {
|
|
const modelName = await getModelName(sink);
|
|
state.modelName = helpers_1.string.pascalCase(helpers_1.string.singularize(modelName.replace(/(\.ts|\.js)$/, '')));
|
|
state.usersTableName = helpers_1.string.pluralize(helpers_1.string.snakeCase(state.modelName));
|
|
state.modelReference = helpers_1.string.camelCase(helpers_1.string.singularize(state.modelName));
|
|
state.modelNamespace = `${app.namespacesMap.get('models') || 'App/Models'}/${state.modelName}`;
|
|
}
|
|
else {
|
|
state.usersTableName = await getTableName(sink);
|
|
}
|
|
const usersMigrationConsent = await getMigrationConsent(sink, state.usersTableName);
|
|
let tokensMigrationConsent = false;
|
|
/**
|
|
* Only ask for the consent when using the api guard
|
|
*/
|
|
if (state.hasGuard.api) {
|
|
state.tokensProvider = await getTokensProvider(sink);
|
|
if (state.tokensProvider === 'database') {
|
|
tokensMigrationConsent = await getMigrationConsent(sink, state.tokensTableName);
|
|
}
|
|
}
|
|
/**
|
|
* Pascal case
|
|
*/
|
|
const camelCaseSchemaName = helpers_1.string.camelCase(`${state.usersTableName}_schema`);
|
|
state.usersSchemaName = `${camelCaseSchemaName
|
|
.charAt(0)
|
|
.toUpperCase()}${camelCaseSchemaName.slice(1)}`;
|
|
/**
|
|
* Make model when prompted for it
|
|
*/
|
|
if (state.modelName) {
|
|
makeModel(projectRoot, app, sink, state);
|
|
}
|
|
/**
|
|
* Make users migration file
|
|
*/
|
|
if (usersMigrationConsent) {
|
|
makeUsersMigration(projectRoot, app, sink, state);
|
|
}
|
|
/**
|
|
* Make tokens migration file
|
|
*/
|
|
if (tokensMigrationConsent) {
|
|
makeTokensMigration(projectRoot, app, sink, state);
|
|
}
|
|
/**
|
|
* Make contract file
|
|
*/
|
|
makeContract(projectRoot, app, sink, state);
|
|
/**
|
|
* Make config file
|
|
*/
|
|
makeConfig(projectRoot, app, sink, state);
|
|
/**
|
|
* Make middleware
|
|
*/
|
|
makeMiddleware(projectRoot, app, sink, state);
|
|
}
|
|
exports.default = instructions;
|