mirror of
https://github.com/h44z/wg-portal.git
synced 2026-08-13 11:16:19 +00:00
Merge commit from fork
This commit is contained in:
@@ -16,16 +16,22 @@ const (
|
||||
)
|
||||
|
||||
type UserAuthenticator interface {
|
||||
IsUserValid(ctx context.Context, id domain.UserIdentifier) bool
|
||||
}
|
||||
|
||||
type UserRepository interface {
|
||||
GetUser(ctx context.Context, id domain.UserIdentifier) (*domain.User, error)
|
||||
}
|
||||
|
||||
type AuthenticationHandler struct {
|
||||
authenticator UserAuthenticator
|
||||
userRepo UserRepository
|
||||
}
|
||||
|
||||
func NewAuthenticationHandler(authenticator UserAuthenticator) AuthenticationHandler {
|
||||
func NewAuthenticationHandler(authenticator UserAuthenticator, userRepo UserRepository) AuthenticationHandler {
|
||||
return AuthenticationHandler{
|
||||
authenticator: authenticator,
|
||||
userRepo: userRepo,
|
||||
}
|
||||
}
|
||||
|
||||
@@ -44,7 +50,7 @@ func (h AuthenticationHandler) LoggedIn(scopes ...Scope) func(next http.Handler)
|
||||
// check if user exists in DB
|
||||
|
||||
ctx := domain.SetUserInfo(r.Context(), domain.SystemAdminContextUserInfo())
|
||||
user, err := h.authenticator.GetUser(ctx, domain.UserIdentifier(username))
|
||||
user, err := h.userRepo.GetUser(ctx, domain.UserIdentifier(username))
|
||||
if err != nil {
|
||||
// Abort the request with the appropriate error code
|
||||
respond.JSON(w, http.StatusUnauthorized,
|
||||
@@ -60,6 +66,13 @@ func (h AuthenticationHandler) LoggedIn(scopes ...Scope) func(next http.Handler)
|
||||
return
|
||||
}
|
||||
|
||||
// ensure that user is still valid
|
||||
if valid := h.authenticator.IsUserValid(r.Context(), domain.UserIdentifier(user.Identifier)); !valid {
|
||||
respond.JSON(w, http.StatusForbidden,
|
||||
model.Error{Code: http.StatusForbidden, Message: "account disabled or locked"})
|
||||
return
|
||||
}
|
||||
|
||||
if !UserHasScopes(user, scopes...) {
|
||||
// Abort the request with the appropriate error code
|
||||
respond.JSON(w, http.StatusForbidden,
|
||||
|
||||
@@ -0,0 +1,116 @@
|
||||
package handlers
|
||||
|
||||
import (
|
||||
"context"
|
||||
"net/http"
|
||||
"net/http/httptest"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/h44z/wg-portal/internal/domain"
|
||||
)
|
||||
|
||||
type stubUserAuthenticator struct {
|
||||
valid bool
|
||||
}
|
||||
|
||||
func (s stubUserAuthenticator) IsUserValid(ctx context.Context, id domain.UserIdentifier) bool {
|
||||
return s.valid
|
||||
}
|
||||
|
||||
type stubUserRepository struct {
|
||||
user *domain.User
|
||||
err error
|
||||
}
|
||||
|
||||
func (s stubUserRepository) GetUser(ctx context.Context, id domain.UserIdentifier) (*domain.User, error) {
|
||||
if s.err != nil {
|
||||
return nil, s.err
|
||||
}
|
||||
|
||||
return s.user, nil
|
||||
}
|
||||
|
||||
func TestAuthenticationHandler_LoggedInRejectsDisabledOrLockedUser(t *testing.T) {
|
||||
tests := []struct {
|
||||
name string
|
||||
configure func(*domain.User)
|
||||
}{
|
||||
{
|
||||
name: "disabled user",
|
||||
configure: func(user *domain.User) {
|
||||
user.Disabled = &[]time.Time{time.Now()}[0]
|
||||
},
|
||||
},
|
||||
{
|
||||
name: "locked user",
|
||||
configure: func(user *domain.User) {
|
||||
user.Locked = &[]time.Time{time.Now()}[0]
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
user := &domain.User{
|
||||
Identifier: "test-user",
|
||||
ApiToken: "token",
|
||||
}
|
||||
tt.configure(user)
|
||||
|
||||
handler := NewAuthenticationHandler(stubUserAuthenticator{valid: false}, stubUserRepository{user: user})
|
||||
nextCalled := false
|
||||
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
nextCalled = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.SetBasicAuth(string(user.Identifier), user.ApiToken)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
handler.LoggedIn()(next).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusForbidden {
|
||||
t.Fatalf("expected status %d, got %d", http.StatusForbidden, rr.Code)
|
||||
}
|
||||
|
||||
if nextCalled {
|
||||
t.Fatal("expected downstream handler not to be called")
|
||||
}
|
||||
|
||||
if !strings.Contains(rr.Body.String(), "account disabled or locked") {
|
||||
t.Fatalf("expected error message to mention disabled or locked, got %q", rr.Body.String())
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticationHandler_LoggedInAllowsValidUser(t *testing.T) {
|
||||
user := &domain.User{
|
||||
Identifier: "test-user",
|
||||
ApiToken: "token",
|
||||
}
|
||||
|
||||
handler := NewAuthenticationHandler(stubUserAuthenticator{valid: true}, stubUserRepository{user: user})
|
||||
nextCalled := false
|
||||
next := http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
|
||||
nextCalled = true
|
||||
w.WriteHeader(http.StatusOK)
|
||||
})
|
||||
|
||||
req := httptest.NewRequest(http.MethodGet, "/", nil)
|
||||
req.SetBasicAuth(string(user.Identifier), user.ApiToken)
|
||||
rr := httptest.NewRecorder()
|
||||
|
||||
handler.LoggedIn()(next).ServeHTTP(rr, req)
|
||||
|
||||
if rr.Code != http.StatusOK {
|
||||
t.Fatalf("expected status %d, got %d", http.StatusOK, rr.Code)
|
||||
}
|
||||
|
||||
if !nextCalled {
|
||||
t.Fatal("expected downstream handler to be called")
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user