Expose user login functionality

This commit is contained in:
Tom Wiesing 2023-01-07 14:31:20 +01:00
parent 97f5ac7e1a
commit 8a5b066839
No known key found for this signature in database
8 changed files with 246 additions and 8 deletions

View file

@ -0,0 +1,109 @@
package next
import (
"context"
"net/http"
"net/url"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/policy"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
"github.com/FAU-CDI/wisski-distillery/internal/wisski"
"github.com/FAU-CDI/wisski-distillery/internal/wisski/ingredient/php/users"
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
)
type Next struct {
component.Base
Dependencies struct {
Auth *auth.Auth
Policy *policy.Policy
Instances *instances.Instances
}
}
var (
_ component.Routeable = (*Next)(nil)
)
func (next *Next) Routes() component.Routes {
return component.Routes{
Paths: []string{"/next/"},
Decorator: next.Dependencies.Auth.Require(auth.User),
}
}
// Next returns a url that will forward authorized users to the given slug and path
func (next *Next) Next(context context.Context, slug, path string) (string, error) {
wisski, err := next.Dependencies.Instances.WissKI(context, slug)
if err != nil {
return "", err
}
target := wisski.URL()
target.Path = path
return "/next/?next=" + url.PathEscape(target.String()), nil
}
func (next *Next) getInstance(r *http.Request) (wisski *wisski.WissKI, path string, err error) {
// extract the instance
url, err := url.Parse(r.URL.Query().Get("next"))
if err != nil {
return nil, "", httpx.ErrBadRequest
}
// find the slug
slug, ok := next.Config.SlugFromHost(url.Host)
if slug == "" || !ok {
return nil, "", httpx.ErrBadRequest
}
// fetch the instance from the database
wisski, err = next.Dependencies.Instances.WissKI(r.Context(), slug)
if err != nil {
return nil, "", err
}
// return the wisski and the relative path
return wisski, url.Path, nil
}
func (next *Next) HandleRoute(ctx context.Context, path string) (http.Handler, error) {
return httpx.RedirectHandler(func(r *http.Request) (string, int, error) {
// get the instance and the path
instance, path, err := next.getInstance(r)
if err != nil {
return "", 0, httpx.ErrForbidden
}
// get the user
user, err := next.Dependencies.Auth.UserOf(r)
if err != nil {
return "", 0, err
}
// check if they have a grant
grant, err := next.Dependencies.Policy.Has(r.Context(), user.User.User, instance.Slug)
if err == policy.ErrNoAccess {
return "", 0, httpx.ErrForbidden
}
if err != nil {
return "", 0, err
}
// perform the login
dest, err := instance.Users().LoginWithOpt(r.Context(), nil, grant.DrupalUsername, users.LoginOptions{
Destination: path,
CreateIfMissing: true,
GrantAdminRole: grant.DrupalAdminRole,
})
if err != nil {
return "", 0, err
}
// and redirect
return dest.String(), http.StatusSeeOther, nil
}), nil
}

View file

@ -6,7 +6,10 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/next"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/policy"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static/custom"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/instances"
"github.com/FAU-CDI/wisski-distillery/internal/models"
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
"github.com/julienschmidt/httprouter"
@ -15,8 +18,11 @@ import (
type UserPanel struct {
component.Base
Dependencies struct {
Auth *auth.Auth
Custom *custom.Custom
Auth *auth.Auth
Custom *custom.Custom
Policy *policy.Policy
Instances *instances.Instances
Next *next.Next
}
}

View file

@ -55,8 +55,49 @@
{{ end }}
<div class="pure-u-1">
There will be a list of WissKIs you have access to here.
<h2>Your WissKIs</h2>
<p>
This is a page of WissKIs you have access to.
Click on the button containing the name to login.
</p>
</div>
<div class="pure-u-1">
<div class="padding">
<div class="overflow">
<table class="pure-table pure-table-bordered pure-form">
<thead>
<tr>
<th>
WissKI Slug
</th>
<th>
Drupal Username
</th>
<th>
Admin
</th>
</tr>
</thead>
<tbody>
{{ range $id, $grant := .Grants }}
<tr>
<td>
<a href="{{ $grant.URL }}" class="pure-button" target="_blank" rel="noopener noreferer">
{{ $grant.Slug }}
</a>
</td>
<td>
{{ $grant.DrupalUsername }}
</td>
<td>
{{ $grant.DrupalAdminRole }}
</td>
</tr>
{{ end }}
</tbody>
</table>
</div>
</div>
</div>
{{ end }}

View file

@ -2,6 +2,7 @@ package panel
import (
"context"
"html/template"
"net/http"
_ "embed"
@ -9,6 +10,7 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control/static/custom"
"github.com/FAU-CDI/wisski-distillery/internal/models"
"github.com/FAU-CDI/wisski-distillery/pkg/httpx"
)
@ -22,6 +24,13 @@ var userTemplate = static.AssetsUser.MustParseShared(
type routeUserContext struct {
custom.BaseContext
*auth.AuthUser
Grants []GrantWithURL
}
type GrantWithURL struct {
models.Grant
URL template.URL
}
func (panel *UserPanel) routeUser(ctx context.Context) http.Handler {
@ -29,7 +38,30 @@ func (panel *UserPanel) routeUser(ctx context.Context) http.Handler {
return &httpx.HTMLHandler[routeUserContext]{
Handler: func(r *http.Request) (ruc routeUserContext, err error) {
panel.Dependencies.Custom.Update(&ruc, r)
// find the user
ruc.AuthUser, err = panel.Dependencies.Auth.UserOf(r)
if err != nil || ruc.AuthUser == nil {
return ruc, err
}
// find the grants
grants, err := panel.Dependencies.Policy.User(r.Context(), ruc.AuthUser.User.User)
if err != nil {
return ruc, err
}
ruc.Grants = make([]GrantWithURL, len(grants))
for i, grant := range grants {
ruc.Grants[i].Grant = grant
url, err := panel.Dependencies.Next.Next(r.Context(), grant.Slug, "/")
if err != nil {
return ruc, err
}
ruc.Grants[i].URL = template.URL(url)
}
return ruc, err
},
Template: userTemplate,

View file

@ -8,6 +8,7 @@ import (
"github.com/FAU-CDI/wisski-distillery/internal/dis/component"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/next"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/panel"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/auth/policy"
"github.com/FAU-CDI/wisski-distillery/internal/dis/component/control"
@ -139,6 +140,7 @@ func (dis *Distillery) allComponents() []initFunc {
auto[*auth.Auth],
auto[*policy.Policy],
auto[*panel.UserPanel],
auto[*next.Next],
// instances
auto[*instances.Instances],