feat: tighten types on newEnforcer arguments
diff --git a/src/cachedEnforcer.ts b/src/cachedEnforcer.ts index 692b236..6953703 100644 --- a/src/cachedEnforcer.ts +++ b/src/cachedEnforcer.ts
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Enforcer, newEnforcerWithClass } from './enforcer'; +import { Enforcer, NewEnforcerParams, newEnforcerWithClass } from './enforcer'; // CachedEnforcer wraps Enforcer and provides decision cache export class CachedEnforcer extends Enforcer { @@ -75,6 +75,6 @@ } // newCachedEnforcer creates a cached enforcer via file or DB. -export async function newCachedEnforcer(...params: any[]): Promise<CachedEnforcer> { +export async function newCachedEnforcer(...params: NewEnforcerParams): Promise<CachedEnforcer> { return newEnforcerWithClass(CachedEnforcer, ...params); }
diff --git a/src/enforcer.ts b/src/enforcer.ts index 57c20f8..fc831a6 100644 --- a/src/enforcer.ts +++ b/src/enforcer.ts
@@ -635,7 +635,30 @@ } } -export async function newEnforcerWithClass<T extends Enforcer>(enforcer: new () => T, ...params: any[]): Promise<T> { +/** + * NewEnforcerParams is the list of argument combinations accepted by the enforcer factories + * (newEnforcer, newEnforcerWithClass, newCachedEnforcer and newSyncedEnforcer). + * + * The model always comes first, either as a path to a `.conf` file or as an already built Model. + * It can be followed by a policy source: a path to a `.csv` file (model file only) or an Adapter. + * A trailing boolean enables the logger, and additionally acts as the `lazyLoad` flag when a model + * file path is combined with an adapter. + */ +export type NewEnforcerParams = + | [] + | [enableLog: boolean] + | [modelPath: string] + | [modelPath: string, enableLog: boolean] + | [modelPath: string, policyPath: string] + | [modelPath: string, policyPath: string, enableLog: boolean] + | [modelPath: string, adapter: Adapter] + | [modelPath: string, adapter: Adapter, lazyLoadAndEnableLog: boolean] + | [model: Model] + | [model: Model, enableLog: boolean] + | [model: Model, adapter: Adapter] + | [model: Model, adapter: Adapter, enableLog: boolean]; + +export async function newEnforcerWithClass<T extends Enforcer>(enforcer: new () => T, ...params: NewEnforcerParams): Promise<T> { // inject the FS if (!getDefaultFileSystem()) { try { @@ -656,36 +679,39 @@ const e = new enforcer(); + // NewEnforcerParams already constrains the shape, index it loosely while dispatching + const args: any[] = params; + let parsedParamLen = 0; - if (params.length >= 1) { - const enableLog = params[params.length - 1]; + if (args.length >= 1) { + const enableLog = args[args.length - 1]; if (typeof enableLog === 'boolean') { getLogger().enableLog(enableLog); parsedParamLen++; } } - if (params.length - parsedParamLen === 2) { - if (typeof params[0] === 'string') { - if (typeof params[1] === 'string') { - await e.initWithFile(params[0].toString(), params[1].toString()); + if (args.length - parsedParamLen === 2) { + if (typeof args[0] === 'string') { + if (typeof args[1] === 'string') { + await e.initWithFile(args[0].toString(), args[1].toString()); } else { - await e.initWithAdapter(params[0].toString(), params[1], params[2] === true); + await e.initWithAdapter(args[0].toString(), args[1], args[2] === true); } } else { - if (typeof params[1] === 'string') { + if (typeof args[1] === 'string') { throw new Error('Invalid parameters for enforcer.'); } else { - await e.initWithModelAndAdapter(params[0], params[1]); + await e.initWithModelAndAdapter(args[0], args[1]); } } - } else if (params.length - parsedParamLen === 1) { - if (typeof params[0] === 'string') { - await e.initWithFile(params[0], ''); + } else if (args.length - parsedParamLen === 1) { + if (typeof args[0] === 'string') { + await e.initWithFile(args[0], ''); } else { - await e.initWithModelAndAdapter(params[0]); + await e.initWithModelAndAdapter(args[0]); } - } else if (params.length === parsedParamLen) { + } else if (args.length === parsedParamLen) { await e.initWithFile('', ''); } else { throw new Error('Invalid parameters for enforcer.'); @@ -710,6 +736,6 @@ * * @param params */ -export async function newEnforcer(...params: any[]): Promise<Enforcer> { +export async function newEnforcer(...params: NewEnforcerParams): Promise<Enforcer> { return newEnforcerWithClass(Enforcer, ...params); }
diff --git a/src/syncedEnforcer.ts b/src/syncedEnforcer.ts index bbc1146..d9922c8 100644 --- a/src/syncedEnforcer.ts +++ b/src/syncedEnforcer.ts
@@ -12,7 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. -import { Enforcer, newEnforcerWithClass } from './enforcer'; +import { Enforcer, NewEnforcerParams, newEnforcerWithClass } from './enforcer'; import AwaitLock from 'await-lock'; import { Watcher } from './persist'; import { MatchingFunc } from './rbac'; @@ -526,6 +526,6 @@ } // newSyncedEnforcer creates a synchronized enforcer via file or DB. -export async function newSyncedEnforcer(...params: any[]): Promise<SyncedEnforcer> { +export async function newSyncedEnforcer(...params: NewEnforcerParams): Promise<SyncedEnforcer> { return newEnforcerWithClass(SyncedEnforcer, ...params); }