verdaccio/docs/dev-plugins.md

6.1 KiB

id title
dev-plugins Developing Plugins

There are many ways to extend verdaccio, the kind of plugins supported are:

  • Authentication plugins
  • Middleware plugins (since v2.7.0)
  • Storage plugins since (v3.x)

We recommend developing plugins using our flow type definitions.

Authentication Plugin

Basically we have to return an object with a single method called authenticate that will recieve 3 arguments (user, password, callback).

API

interface IPluginAuth extends IPlugin {
  login_url?: string;
  authenticate(user: string, password: string, cb: Callback): void;
  adduser(user: string, password: string, cb: Callback): void;
  allow_access(user: RemoteUser, pkg: $Subtype<PackageAccess>, cb: Callback): void;
  allow_publish(user: RemoteUser, pkg: $Subtype<PackageAccess>, cb: Callback): void;
}

Only adduser, allow_access and allow_publish are optional, verdaccio provide a fallback in all those cases.

Callback

Once the authentication has been executed there is 2 options to give a response to verdaccio.

OnError

Either something bad happened or auth was unsuccessful.

callback(null, false)
OnSuccess

The auth was successful.

groups is an array of strings where the user is part of.

 callback(null, groups);

Example

function Auth(config, stuff) {
  var self = Object.create(Auth.prototype);
  self._users = {};

  // config for this module
  self._config = config;

  // verdaccio logger
  self._logger = stuff.logger;

  // pass verdaccio logger to ldapauth
  self._config.client_options.log = stuff.logger;

  return self;
}

Auth.prototype.authenticate = function (user, password, callback) {
  var LdapClient = new LdapAuth(self._config.client_options);
  ....
  LdapClient.authenticate(user, password, function (err, ldapUser) {
    ...
    var groups;
     ...
    callback(null, groups);
  });
};

module.exports = Auth;

And the configuration will looks like:

auth:
  htpasswd:
    file: ./htpasswd

Where htpasswd is the sufix of the plugin name. eg: verdaccio-htpasswd and the rest of the body would be the plugin configuration params.

Middleware Plugin

Middleware plugins have the capability to modify the API layer, either adding new endpoints or intercepting requests.

interface verdaccio$IPluginMiddleware extends verdaccio$IPlugin {
  register_middlewares(app: any, auth: IBasicAuth, storage: IStorageManager): void;
}

register_middlewares

The method provide full access to the authentification and storage via auth and storage. app is the express application that allows you to add new endpoints.

A pretty good example of middleware plugin is the sinopia-github-oauth and verdaccio-audit.

API

function register_middlewares(expressApp, authInstance, storageInstance) {
   /* more stuff */
}

To register a middleware we need an object with a single method called register_middlewares that will recieve 3 arguments (expressApp, auth, storage). Auth is the authentification instance and storage is also the main Storage instance that will give you have access to all to the storage actions.

Storage Plugin

Verdaccio by default uses a file system storage plugin local-storage, but, since verdaccio@3.x you can plug in a custom storage replacing the default behaviour.

API

The storage API is a bit more complex, you will need to create a class that return a IPluginStorage implementation. Please see details bellow.

class LocalDatabase<IPluginStorage>{
  constructor(config: $Subtype<verdaccio$Config>, logger: verdaccio$Logger): ILocalData;
}

interface IPluginStorage {
  logger: verdaccio$Logger;
	config: $Subtype<verdaccio$Config>;
  add(name: string, callback: verdaccio$Callback): void;
  remove(name: string, callback: verdaccio$Callback): void;
  get(callback: verdaccio$Callback): void;
  getSecret(): Promise<string>;
  setSecret(secret: string): Promise<any>;
  getPackageStorage(packageInfo: string): verdaccio$IPackageStorage;
  search(onPackage: verdaccio$Callback, onEnd: verdaccio$Callback, validateName: Function): void;
}

interface IPackageStorageManager {
  path: string;
  logger: verdaccio$Logger;
  writeTarball(name: string): verdaccio$IUploadTarball;
  readTarball(name: string): verdaccio$IReadTarball;
  readPackage(fileName: string, callback: verdaccio$Callback): void;
  createPackage(name: string, value: verdaccio$Package, cb: verdaccio$Callback): void;
  deletePackage(fileName: string, callback: verdaccio$Callback): void;
  removePackage(callback: verdaccio$Callback): void;
  updatePackage(pkgFileName: string,
                updateHandler: verdaccio$Callback,
                onWrite: verdaccio$Callback,
                transformPackage: Function,
                onEnd: verdaccio$Callback): void;
  savePackage(fileName: string, json: verdaccio$Package, callback: verdaccio$Callback): void;
}

class verdaccio$IUploadTarball extends stream$PassThrough {
  abort: Function;
  done: Function;
  _transform: Function;
  abort(): void;
  done(): void;
}

class verdaccio$IReadTarball extends stream$PassThrough {
  abort: Function;
  abort(): void;
}

The Storage API is still experimental and might change in the next minor versions. For further information about Storage API please follow the types definitions in our official repository.

Storage Plugins Examples

The following list of plugins are implementing the Storage API and might be used them as example.

Are you willing to contribute with new Storage Plugins? Click here.