... | ... | @@ -167,12 +167,12 @@ classDiagram |
|
|
|
|
|
HookModule -- FooCallout
|
|
|
HookModule -- BarCallout
|
|
|
HookModule -- BazCallout
|
|
|
HookModule -- BazCallout
|
|
|
HookModule -- IHook
|
|
|
|
|
|
AgentHookManager *-- HookExecutor
|
|
|
ServerHookManager *-- HookExecutor
|
|
|
|
|
|
|
|
|
ServerCore ..> ServerHookManager
|
|
|
ServerCore ..> SettingsObserver
|
|
|
AgentCore ..> AgentHookManager
|
... | ... | @@ -183,7 +183,7 @@ classDiagram |
|
|
BazCallout <|.. ServerHookManager
|
|
|
|
|
|
HookExecutor .. HookLoader
|
|
|
|
|
|
|
|
|
HookLoader ..> LibraryManager
|
|
|
HookLoader ..> SettingsObserver
|
|
|
HookLoader ..> SettingsRegistry
|
... | ... | @@ -201,7 +201,7 @@ classDiagram |
|
|
BarBazImpl ..|> BazCallout
|
|
|
```
|
|
|
|
|
|
The code is distributed over four packages:
|
|
|
The code is distributed over four packages:
|
|
|
|
|
|
- hook module - it contains the interfaces and is shared between core and hook libraries
|
|
|
- plugin package - the hook library implementations
|
... | ... | @@ -210,7 +210,7 @@ The code is distributed over four packages: |
|
|
|
|
|
The hook module [`HookModule`] defines the primary hook interface [`IHook`]. It specifies that each hook should provide the `Version` and `Load` functions. The `Version` function returns the application name and version with which the plugin is compatible. The `Load` function produces an object that implements the callout points. The supported callout points are defined in the hook module as Go interfaces [`FooCallout`/`BarCallout`/`BazCallout`].
|
|
|
|
|
|
Additionally, the hook may implement the `SetupCLI()`. It will be called before loading the hook. It's responsible for preparing the hook-specific CLI parameters. They will be merged with the core parameters. The provided CLI arguments will be passed to the `Load` function.
|
|
|
Additionally, the hook may implement the `SetupCLI()`. It will be called before loading the hook. It's responsible for preparing the hook-specific CLI parameters. They will be merged with the core parameters. The provided CLI arguments will be passed to the `Load` function.
|
|
|
The `SetupSettings` function accepts settings registry [`SettingsRegistry`]. This object allows registering the dynamic settings used by the hook. Users will have a possibility to change these values using UI (for server). Initially, it will not be used for the agent hooks.
|
|
|
|
|
|
The `Load` function accepts the observer of settings [`SettingsObserver`]. It may be used to register on settings notifications. The given function will be called on change of the specific setting.
|
... | ... | @@ -260,10 +260,10 @@ The hook provides a function that returns the CLI flags. This function is called |
|
|
|
|
|
### Hook loading time
|
|
|
|
|
|
The first time when the hooks are loaded is the server setup phase. The hook manager should load all hooks from the specific directory.
|
|
|
The first time when the hooks are loaded is the server setup phase. The hook manager should load all hooks from the specific directory.
|
|
|
It's possible to watch on the hook directory. When the user pastes the new hook library, the application can load it automatically. If it isn't an expected behavior, the new hooks may be loaded on demand (e.g., requested via the RESTApi).
|
|
|
|
|
|
The first time when the hooks are loaded is the server setup phase. The hook manager should load all hooks from the specific directory.
|
|
|
The first time when the hooks are loaded is the server setup phase. The hook manager should load all hooks from the specific directory.
|
|
|
It's possible to watch on the hook directory. When the user pastes the new hook library, the application can load it automatically. If it isn't an expected behavior, the new hooks may be loaded on demand (e.g., requested via the RESTApi). The look loaded in runtime cannot use the CLI parameters, but it still can read environment variables or query the settings database.
|
|
|
|
|
|
### Comparison
|
... | ... | @@ -304,7 +304,7 @@ The context provides the timeout feature. It may be used to interrupt hooks that |
|
|
|
|
|
### Exchange data using callout signature
|
|
|
|
|
|
In this case, the data sharing is controlled by the core. It means that the core knows the data meaning and may interrupt the hook execution or modify the data between calling the callout points.
|
|
|
In this case, the data sharing is controlled by the core. It means that the core knows the data meaning and may interrupt the hook execution or modify the data between calling the callout points.
|
|
|
|
|
|
There are two approaches to implementation. The first uses a single mutable object that is passed to callout points. It's similar to the data exchange using the context described in the previous section. The main difference is the core knows the structure of the data and may analyze its content. The second approach is passing the output of the previous callout as the arguments of the current callout. These solutions are very similar and differ mainly in code clarity and testability.
|
|
|
|
... | ... | @@ -427,7 +427,7 @@ There should also be statistics collected per callout to monitor performance acr |
|
|
```go
|
|
|
func Close() error {
|
|
|
return &Callouts{}, nil
|
|
|
}
|
|
|
}
|
|
|
```
|
|
|
|
|
|
8. Compile to a plugin file
|
... | ... | @@ -457,7 +457,7 @@ We decided the first hook would be an authentication hook to support login throu |
|
|
|
|
|
In general, we can split the authentication methods into two groups. The first contains solutions where the server actively mediates in the authentication process. It means that a user provides the credentials in the form presented by the server, and next, the server forwards these credentials to the authentication point. The authentication point validates the credentials and returns the user profile on success. The solutions in the second group use the delegated flow. The user initiates the authentication by clicking the button on the server page. The server redirects to the authentication point page. The user provides the credentials in the form presented by the authentication point that validates the credentials and redirects the user back to the server. The server receives the authentication token that is validated on the server-side. The token may contain the basic user profile, or the server may use it to obtain the profile from the authentication point.
|
|
|
|
|
|
The LDAP authentication belongs to the first group.
|
|
|
The LDAP authentication belongs to the first group.
|
|
|
|
|
|
The authentication process in Stork starts on the login page. There should be displayed a list of supported authentication methods generated by the dedicated callout. Stork should also provide a flag to suppress the default authentication. The user needs to choose the preferred authentication method. Another approach is allowing only a single authentication method. If the authentication hook is loaded, it replaces the default authentication; only one authentication hook may be loaded simultaneously. The Stork should present a login form or redirect the user to the authentication point page, depending on the selected solution. The hook will be responsible for validating the form data, fetching the profile, and creating the internal user instance. This instance will be passed to the session manager. The hook should provide the possibility to log out the user too.
|
|
|
In the case of delegated authentication, the callout must generate the redirection link and handle the returned token validation.
|
... | ... | @@ -468,7 +468,7 @@ It is a single authentication solution. |
|
|
|
|
|
1. The server loads the hooks, including the authentication one
|
|
|
2. The user opens the login page
|
|
|
3. The UI fetches the authentication method details from the server. It displays the login form or redirection button to the authentication point.
|
|
|
3. The UI fetches the authentication method details from the server. It displays the login form or redirection button to the authentication point.
|
|
|
4. The server calls the callout point to fetch the authentication details. If no authentication hooks were loaded, then it returns the default method. For LDAP, the login and password form is requested.
|
|
|
5. The UI presents the login and password form.
|
|
|
6. The user provides the credentials and clicks the submit button.
|
... | ... | @@ -479,7 +479,7 @@ It is a single authentication solution. |
|
|
11. The server creates a session in the session manager.
|
|
|
12. The server returns the user object to the UI.
|
|
|
13. The session middleware sets the authentication cookie.
|
|
|
14. The browser receives the response, reads the cookie, and remembers it.
|
|
|
14. The browser receives the response, reads the cookie, and remembers it.
|
|
|
15. The UI receives the user object, saves it, and redirects to the dashboard.
|
|
|
16. The user uses Stork.
|
|
|
17. The user clicks the logout button.
|
... | ... | @@ -505,8 +505,8 @@ It is a single authentication solution. |
|
|
|
|
|
This callout point will call only the first hook. If no hooks are registered, the core will fall back to the standard authentication.
|
|
|
|
|
|
2. `Authenticate(params users.CreateSessionParams) (*SystemUser, error)`
|
|
|
|
|
|
2. `Authenticate(params users.CreateSessionParams) (*SystemUser, error)`
|
|
|
|
|
|
The authenticate callout point will be responsible for validating the credentials and preparing the user instance. It should send the data to the LDAP server. If they are correct, then the hook should fetch the user profile. It must translate the LDAP roles to the Stork groups.
|
|
|
|
|
|
This callout point will be called in the `CreateSession` REST handler. Only the callout from the first hook will be used.
|
... | ... | @@ -748,7 +748,7 @@ func (c *callouts) Authenticate(ctx context.Context, params users.CreateSessionP |
|
|
|
|
|
This code checks the provided credentials (username: `secret`, password: `secret`). It returns a user profile on success or authentication error on failure.
|
|
|
|
|
|
The plugin contains one more function, `Unathenticate`, with an empty implementation.
|
|
|
The plugin contains one more function, `Unauthenticate`, with an empty implementation.
|
|
|
|
|
|
```go
|
|
|
func (c *callouts) Unauthenticate(ctx context.Context) error {
|
... | ... | @@ -758,7 +758,7 @@ func (c *callouts) Unauthenticate(ctx context.Context) error { |
|
|
|
|
|
The plugin is ready and may be compiled.
|
|
|
|
|
|
The hook manager implements the authentication callouts on the core side. It defers the execution to the plugin if any is registered. In this example, only a single authentication plugin may be used. The hook manager is used by the RestAPI handler when the session starts.
|
|
|
The hook manager implements the authentication callouts on the core side. It defers the execution to the plugin if any is registered. In this example, only a single authentication plugin may be used. The hook manager is used by the RestAPI handler when the session starts.
|
|
|
|
|
|
```go
|
|
|
func (r *RestAPI) CreateSession(ctx context.Context, params users.CreateSessionParams) middleware.Responder {
|
... | ... | |