... | ... | @@ -438,7 +438,7 @@ There should also be statistics collected per callout to monitor performance acr |
|
|
|
|
|
Golang requires that the core and plugin dependencies will be in precisely identical versions. The easiest method to fill this requirement is to add the core as a plugin's dependency. Unfortunately, the Stork core doesn't exist in the Go package registry and cannot be added in the standard way. There is a possibility to specify the dependency by the relative path by using the `go mod replace` command.
|
|
|
|
|
|
|
|
|
Golang also requires that the core and plugins will be built with the same compilation flags. It isn't a big deal because we don't use any custom flags in the Stork except for the debugging process. The debug binaries are compiled with the flags injected by the delve (`dlv`) debugger.
|
|
|
|
|
|
## Authentication hook
|
|
|
|
... | ... | @@ -635,3 +635,148 @@ func (sa *StorkAgent) Shutdown() { |
|
|
...
|
|
|
}
|
|
|
```
|
|
|
|
|
|
### Simple authentication hook
|
|
|
|
|
|
It is an example of the most straightforward authentication hook. It has a hardcoded login and password.
|
|
|
|
|
|
We prepare the basic interface in the core hook module to define the authentication callout points.
|
|
|
|
|
|
```go
|
|
|
type AuthenticationCallout interface {
|
|
|
Authenticate(ctx context.Context, params users.CreateSessionParams) (*dbmodel.SystemUser, error)
|
|
|
Unauthenticate(ctx context.Context) error
|
|
|
}
|
|
|
```
|
|
|
|
|
|
Next, we create the hook. We need to generate the `go.mod` file and specify the core as a dependency. We need to use the replace statement because the Stork core isn't available in the Go package repository.
|
|
|
|
|
|
```go
|
|
|
module isc.org/dumb-authentication-hook
|
|
|
|
|
|
go 1.18
|
|
|
|
|
|
replace isc.org/stork v0.0.0 => ../../backend
|
|
|
|
|
|
require (
|
|
|
isc.org/stork v0.0.0
|
|
|
)
|
|
|
```
|
|
|
|
|
|
Now, we create two files: `plugin.go` and `callouts.go`. The first file will contain standard `Load` and `Version` implementations.
|
|
|
|
|
|
```go
|
|
|
package main
|
|
|
|
|
|
import (
|
|
|
"isc.org/stork/hooks"
|
|
|
)
|
|
|
|
|
|
func Load() (hooks.Closer, error) {
|
|
|
return &callouts{}, nil
|
|
|
}
|
|
|
|
|
|
func Version() (string, string) {
|
|
|
return hooks.HookProgramServer, hooks.StorkVersion
|
|
|
}
|
|
|
|
|
|
// Type guards
|
|
|
var _ hooks.HookLoadFunction = Load
|
|
|
var _ hooks.HookVersionFunction = Version
|
|
|
```
|
|
|
|
|
|
We start implementing the second file with an empty structure.
|
|
|
We must specify the interface checks to ensure the authentication callouts are correctly implemented. First, we need to import a specific callout definition from the core.
|
|
|
|
|
|
```go
|
|
|
import (
|
|
|
"isc.org/stork/hooks/server/authenticationcallout"
|
|
|
)
|
|
|
```
|
|
|
|
|
|
Now, we can add the interface check.
|
|
|
|
|
|
```go
|
|
|
var _ authenticationcallout.AuthenticationCallout = (*callouts)(nil)
|
|
|
```
|
|
|
|
|
|
We shouldn't forget about implementing a Closer interface in the structure. It will do nothing, but it's mandatory.
|
|
|
|
|
|
```go
|
|
|
func (c *callouts) Close() error {
|
|
|
return nil
|
|
|
}
|
|
|
```
|
|
|
|
|
|
We must now implement the authentication callout points required by the interface check. In minimal solution, it contains two functions. One for authenticating a user and another for closing the logged session (unauthenticating).
|
|
|
|
|
|
For example purposes, the authentication will have only one valid credentials. They will be hardcoded in the plugin.
|
|
|
|
|
|
```go
|
|
|
func (c *callouts) Authenticate(ctx context.Context, params users.CreateSessionParams) (*dbmodel.SystemUser, error) {
|
|
|
if params.Credentials.Useremail == nil || params.Credentials.Userpassword == nil {
|
|
|
return nil, errors.Errorf("missing email or password")
|
|
|
}
|
|
|
if *params.Credentials.Useremail != "secret" || *params.Credentials.Userpassword != "secret" {
|
|
|
return nil, errors.Errorf("invalid user or password")
|
|
|
}
|
|
|
|
|
|
return &dbmodel.SystemUser{
|
|
|
ID: 1,
|
|
|
Login: "Secretary",
|
|
|
Email: "secretary@office.com",
|
|
|
Lastname: "Bob",
|
|
|
Name: "Alice",
|
|
|
Password: "",
|
|
|
Groups: []*dbmodel.SystemGroup{
|
|
|
&dbmodel.SystemGroup{ID: dbmodel.SuperAdminGroupID},
|
|
|
},
|
|
|
}, nil
|
|
|
}
|
|
|
```
|
|
|
|
|
|
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.
|
|
|
|
|
|
```go
|
|
|
func (c *callouts) Unauthenticate(ctx context.Context) error {
|
|
|
return nil
|
|
|
}
|
|
|
```
|
|
|
|
|
|
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.
|
|
|
|
|
|
```go
|
|
|
func (r *RestAPI) CreateSession(ctx context.Context, params users.CreateSessionParams) middleware.Responder {
|
|
|
var user *dbmodel.SystemUser
|
|
|
var err error
|
|
|
|
|
|
if r.HookManager.HasAuthenticationHook() {
|
|
|
user, err = r.HookManager.Authenticate(ctx, params)
|
|
|
} else {
|
|
|
user, err = r.defaultAuthentication(params)
|
|
|
}
|
|
|
|
|
|
if user != nil {
|
|
|
err = r.SessionManager.LoginHandler(ctx, user)
|
|
|
}
|
|
|
|
|
|
...
|
|
|
}
|
|
|
```
|
|
|
|
|
|
If the authentication hook is not loaded, then the default authentication is used. The returned user profile is passed to the session manager, who creates the authentication cookie.
|
|
|
|
|
|
The hook manager is also called during deleting session. The hook may perform any additional action on this event.
|
|
|
|
|
|
```go
|
|
|
func (r *RestAPI) DeleteSession(ctx context.Context, params users.DeleteSessionParams) middleware.Responder {
|
|
|
err := r.SessionManager.LogoutHandler(ctx)
|
|
|
...
|
|
|
r.HookManager.Unauthenticate(ctx)
|
|
|
...
|
|
|
}
|
|
|
``` |