Plugin Development Guide for PantherX Online Account Service
PantherX Online Account Service supports a Plugin System to allow third-party applications to add their Account Management integrated to it.
In this document we provide information and resources that needed for this purpose.
Plugin System consist of a PluginManager
class which dynamically loads registered
plugins from file system during application start and call them during
Account Service Param Verification and Account Service Authentication tasks.
For now Plugin System supports C++
and Python
based plugins, based on your
preferred language, you could continue reading about following sections:
Each Python Plugin is actually just a python module that implements an interface provided by Plugin System.
When PluginManager
loads a python-based plugin, PluginFramework
module will be
added dynamically. this module is implemented on Account Service with a series
of types and interfaces. so you need to load this module dynamically inside your
plugin definition to prevent error occurrence:
import pkgutil
if pkgutil.find_loader('PluginFramework') is not None:
PluginFramework = importlib.import_module('PluginFramework')
we will continue with describing PluginFramework
types and interfaces:
PluginFramework.StrStrMap
This type is equivalent to std::map<std::string, std::string>
container in C++.
we use that to return string based dictionaries to parent application.
PluginFramework.StringList
This type is equivalent to std::vector<std::string>
container in C++.
we use that to return list of strings to parent application.
PluginFramework.Plugin
Plugin
is base class for module development. you need to inherit from it
in order to create your plugin. Each plugin has two abstract methods: verify
and authenticate
.
Important: your derived class should named as Plugin
in order to allow
parent Plugin Loader to load it.
class Plugin(PluginFramework.Plugin):
# Plugin title that account service check to find plugins
title = 'plugin-name'
def __init__(self):
# Plugin initiation logic goes here
pass
def verify(self, params):
# your service parameter verification logic goes here
pass
def authenticate(self, params):
# your service authentication logic goes here
pass
PluginFramework.ServiceParam
This struct
represents a parsed service parameter, parameter relaed flags will
add to this class beside it’s key and value:
class ServiceParam:
key # String
val # String
is_required # Boolean
is_protected # Boolean
PluginFramework.ServiceParamList
This type is equivalent to std::vector<ServiceParam>
in C++. we use this struct
to return a list of ServiceParam
items to Account service.
PluginFramework.VerifyResult
This class contains parameters related to raw data verification results. it holds verification status flag, parsed parameters and list of errors occured during plugin verification:
class VerifyResult:
verified # Boolean
params # ServiceParamList
errors # StringList
PluginFramework.AuthResult
This class contains parameters related to plugin authentication result. it holds authentication status flag, generated tokens during authentication process and list of occurred errors:
class AuthResult:
authenticated # Boolean
tokens # StrStrMap
errors # StringList
PluginFramework.Plugin.verify
This method is used to verify raw key-value parameters provided. it receives a
StrStrMap
as input parameters and return a VerifyResult
structure in response:
def verify(self, params):
result = PluginFramework.VerifyResult()
result.errors.append('method not implemented.')
result.verified = False
return result
PluginFramework.Plugin.authenticate
This method is used to for plugin authentication process. this method receives
a ServiceParamList
struct as input and returns a AuthResult
class as response.
def authenticate(self, params):
result = PluginFramework.AuthResult()
result.authenticated = False
result.errors.append('method not implemented.')
return result
PluginFramework.Plugin.read
Implementing this method, your plugin read it’s parameters from it’s defined custom
data-source instead of default Account Configuration File. read
method receives
a unique identifier previously generated by plugin, and should retrun a
StrStrMap
of plugin parameters.
def read(self, id):
result = PluginFramework.StrStrMap()
# Do whatever needs to fill 'result' here...
return result
PluginFramework.Plugin.write
Implementing this method, your plugin writes received parameters to a custom data-source, and return a unique identifier related to it’s write operation. this unique identifier later will use for plugin details read or remove.
def write(self, vResult, aResult):
# Write parameters to whatever data-source you want and generate a
# Unique ID for it. this Id needs to be returned to parent application.
id = ...
return id
PluginFramework.Plugin.remove
this method calls during account removal, so plugin should remove whatever it, previously generates using this method.
def remove(self, id):
# perform plugin cleanup task here and return it's status
return True
Each C++ Based Plugin is a C++
class which is derived form IPlugin
interface.
we need to export this class definition as a Shared Object.
additionally implementing the read
, write
and remove
virtual methods allows
the plugin to perform custom read/write operations.
class IPlugin {
public:
explicit IPlugin() = default;
explicit IPlugin(const string &title) : title(title) {}
virtual ~IPlugin() = default;
virtual VerifyResult verify(const StrStrMap ¶ms) = 0;
virtual AuthResult authenticate(const ServiceParamList ¶ms) = 0;
virtual StrStrMap read(const string &id);
virtual string write(VerifyResult &vResult, AuthResult &aResult);
virtual bool remove(const string &id);
public:
string title;
};
we need to implement constructor
, verify
and authenticate
methods in our plugins.
definition of these methods is same as their python equivalent definitions:
explicit PluginClass() : IPlugin("plugin-name") {}
virtual VerifyResult verify(const StrStrMap ¶ms);
virtual AuthResult authenticate(const ServiceParamList ¶ms);
after implementing our plugin, we need to export it’s reference:
#include "plugin-class.h"
PluginClass *allocator() {
return new PluginClass();
}
void deleter(PluginClass* ptr) {
delete ptr;
}
now that your plugin implementation is done, you need to build your plugin as a Shared Library.
In order to load protected parameters automatically during account modification,
you need tho add them as ServiceParam
with Empty Value to list of VerifyResult.params
.
in this way Account Service request for value of these parameters from
Secret Service before calling the authenticate
method.
If you want to develop a plugin that is responsible for it’s own configurations,
you need to implement read
, write
and remove
methods in your plugin
definition. using this methods, your plugin handles your account configuration
management by itself.
After creating your plugin, you need to register in order to be able to load by
Online Accounts Service. for this you need to add a custom phase after install
called register-plugin
in your package definition. in this phase you should
register your plugin:
(add-after 'install 'register-plugins
(lambda* (#:key inputs outputs #:allow-other-keys)
(let* ((out (assoc-ref outputs "out"))
(regpath (string-append out "/etc/px/accounts/plugins"))
(regdata (string-append "plugin:\n"
" name: " ,name "\n"
" version:" ,version "\n"
" type: cpp\n" ; allowed values are:
; - "python"
; - "cpp"
" path:" out "/lib/lib" ,name ".so\n")))
(mkdir-p regpath)
(display cpptest-data)
(with-output-to-file (string-append regpath "/" cpptest-name ".yaml")
(lambda _ (format #t cpptest-data))))))
PantherX & (unofficial) GNU Guix Wiki.
Last update: 2023-11-11 21:56:46 +0000 | Apache-2.0
Inspired by the excellent Arch Linux Wiki