Merge branch 'eb/cookiename-configurable' of _OKE5H2PQKOUfzFFDuD4FA/default/CODE/gitness (#573)

This commit is contained in:
Enver Bisevac 2023-09-20 15:00:18 +00:00 committed by Harness
commit 138f47dd92
11 changed files with 44 additions and 39 deletions

View File

@ -79,6 +79,7 @@ ENV GITNESS_DATABASE_DRIVER sqlite3
ENV GITNESS_DATABASE_DATASOURCE /data/database.sqlite
ENV GITNESS_METRIC_ENABLED=true
ENV GITNESS_METRIC_ENDPOINT=https://stats.drone.ci/api/v1/gitness
ENV GITNESS_TOKEN_COOKIE_NAME=token
COPY --from=builder /app/gitness /app/gitness
COPY --from=cert-image /etc/ssl/certs/ca-certificates.crt /etc/ssl/certs/ca-certificates.crt

View File

@ -94,7 +94,7 @@ func initSystem(ctx context.Context, config *types.Config) (*server.System, erro
controller := user.ProvideController(db, principalUID, authorizer, principalStore, tokenStore, membershipStore)
serviceController := service.NewController(principalUID, authorizer, principalStore)
bootstrapBootstrap := bootstrap.ProvideBootstrap(config, controller, serviceController)
authenticator := authn.ProvideAuthenticator(principalStore, tokenStore)
authenticator := authn.ProvideAuthenticator(config, principalStore, tokenStore)
provider, err := url.ProvideURLProvider(config)
if err != nil {
return nil, err

View File

@ -9,12 +9,15 @@ import (
"net/http"
"time"
"github.com/harness/gitness/internal/api/request"
"github.com/harness/gitness/types"
)
func includeTokenCookie(r *http.Request, w http.ResponseWriter, tokenResponse *types.TokenResponse) {
cookie := newEmptyTokenCookie(r)
func includeTokenCookie(
r *http.Request, w http.ResponseWriter,
tokenResponse *types.TokenResponse,
cookieName string,
) {
cookie := newEmptyTokenCookie(r, cookieName)
cookie.Value = tokenResponse.AccessToken
if tokenResponse.Token.ExpiresAt != nil {
cookie.Expires = time.UnixMilli(*tokenResponse.Token.ExpiresAt)
@ -23,24 +26,24 @@ func includeTokenCookie(r *http.Request, w http.ResponseWriter, tokenResponse *t
http.SetCookie(w, cookie)
}
func deleteTokenCookieIfPresent(r *http.Request, w http.ResponseWriter) {
func deleteTokenCookieIfPresent(r *http.Request, w http.ResponseWriter, cookieName string) {
// if no token is present in the cookies, nothing todo.
// No other error type expected here - and even if there is, let's try best effort deletion.
_, err := r.Cookie(request.CookieToken)
_, err := r.Cookie(cookieName)
if errors.Is(err, http.ErrNoCookie) {
return
}
cookie := newEmptyTokenCookie(r)
cookie := newEmptyTokenCookie(r, cookieName)
cookie.Value = ""
cookie.Expires = time.UnixMilli(0) // this effectively tells the browser to delete the cookie
http.SetCookie(w, cookie)
}
func newEmptyTokenCookie(r *http.Request) *http.Cookie {
func newEmptyTokenCookie(r *http.Request, cookieName string) *http.Cookie {
return &http.Cookie{
Name: request.CookieToken,
Name: cookieName,
SameSite: http.SameSiteStrictMode,
HttpOnly: true,
Path: "/",

View File

@ -15,19 +15,13 @@ import (
// HandleLogin returns an http.HandlerFunc that authenticates
// the user and returns an authentication token on success.
func HandleLogin(userCtrl *user.Controller) http.HandlerFunc {
func HandleLogin(userCtrl *user.Controller, cookieName string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
includeCookie, err := request.GetIncludeCookieFromQueryOrDefault(r, false)
if err != nil {
render.TranslatedUserError(w, err)
return
}
in := new(user.LoginInput)
err = json.NewDecoder(r.Body).Decode(in)
err := json.NewDecoder(r.Body).Decode(in)
if err != nil {
render.BadRequestf(w, "Invalid request body: %s.", err)
return
@ -39,8 +33,8 @@ func HandleLogin(userCtrl *user.Controller) http.HandlerFunc {
return
}
if includeCookie {
includeTokenCookie(r, w, tokenResponse)
if cookieName != "" {
includeTokenCookie(r, w, tokenResponse, cookieName)
}
render.JSON(w, http.StatusOK, tokenResponse)

View File

@ -14,7 +14,7 @@ import (
// HandleLogout returns a http.HandlerFunc that deletes the
// user token being used in the respective request and logs the user out.
func HandleLogout(userCtrl *user.Controller) http.HandlerFunc {
func HandleLogout(userCtrl *user.Controller, cookieName string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
session, _ := request.AuthSessionFrom(ctx)
@ -24,7 +24,7 @@ func HandleLogout(userCtrl *user.Controller) http.HandlerFunc {
// best effort delete cookie even in case of errors, to avoid clients being unable to remove the cookie.
// WARNING: It could be that the cookie is removed even though the token is still there in the DB.
// However, we have APIs to list and delete session tokens, and expiry time is usually short.
deleteTokenCookieIfPresent(r, w)
deleteTokenCookieIfPresent(r, w, cookieName)
if err != nil {
render.TranslatedUserError(w, err)

View File

@ -16,7 +16,7 @@ import (
// HandleRegister returns an http.HandlerFunc that processes an http.Request
// to register the named user account with the system.
func HandleRegister(userCtrl *user.Controller, sysCtrl *system.Controller) http.HandlerFunc {
func HandleRegister(userCtrl *user.Controller, sysCtrl *system.Controller, cookieName string) http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
@ -40,7 +40,7 @@ func HandleRegister(userCtrl *user.Controller, sysCtrl *system.Controller) http.
}
if includeCookie {
includeTokenCookie(r, w, tokenResponse)
includeTokenCookie(r, w, tokenResponse, cookieName)
}
render.JSON(w, http.StatusOK, tokenResponse)

View File

@ -11,7 +11,6 @@ import (
const (
QueryParamAccessToken = "access_token"
QueryParamIncludeCookie = "include_cookie"
CookieToken = "token"
)
func GetAccessTokenFromQuery(r *http.Request) (string, bool) {
@ -22,6 +21,6 @@ func GetIncludeCookieFromQueryOrDefault(r *http.Request, dflt bool) (bool, error
return QueryParamAsBoolOrDefault(r, QueryParamIncludeCookie, dflt)
}
func GetTokenFromCookie(r *http.Request) (string, bool) {
return GetCookie(r, CookieToken)
func GetTokenFromCookie(r *http.Request, cookieName string) (string, bool) {
return GetCookie(r, cookieName)
}

View File

@ -24,14 +24,18 @@ var _ Authenticator = (*JWTAuthenticator)(nil)
// JWTAuthenticator uses the provided JWT to authenticate the caller.
type JWTAuthenticator struct {
cookieName string
principalStore store.PrincipalStore
tokenStore store.TokenStore
}
func NewTokenAuthenticator(
principalStore store.PrincipalStore,
tokenStore store.TokenStore) *JWTAuthenticator {
tokenStore store.TokenStore,
cookieName string,
) *JWTAuthenticator {
return &JWTAuthenticator{
cookieName: cookieName,
principalStore: principalStore,
tokenStore: tokenStore,
}
@ -39,7 +43,7 @@ func NewTokenAuthenticator(
func (a *JWTAuthenticator) Authenticate(r *http.Request, sourceRouter SourceRouter) (*auth.Session, error) {
ctx := r.Context()
str := extractToken(r)
str := extractToken(r, a.cookieName)
if len(str) == 0 {
return nil, ErrNoAuthData
@ -122,7 +126,7 @@ func (a *JWTAuthenticator) metadataFromMembershipClaims(
}, nil
}
func extractToken(r *http.Request) string {
func extractToken(r *http.Request, cookieName string) string {
// Check query param first (as that's most immediately visible to caller)
if queryToken, ok := request.GetAccessTokenFromQuery(r); ok {
return queryToken
@ -145,7 +149,7 @@ func extractToken(r *http.Request) string {
}
// check cookies last (as that's least visible to caller)
if cookieToken, ok := request.GetTokenFromCookie(r); ok {
if cookieToken, ok := request.GetTokenFromCookie(r, cookieName); ok {
return cookieToken
}

View File

@ -6,6 +6,7 @@ package authn
import (
"github.com/harness/gitness/internal/store"
"github.com/harness/gitness/types"
"github.com/google/wire"
)
@ -15,6 +16,6 @@ var WireSet = wire.NewSet(
ProvideAuthenticator,
)
func ProvideAuthenticator(principalStore store.PrincipalStore, tokenStore store.TokenStore) Authenticator {
return NewTokenAuthenticator(principalStore, tokenStore)
func ProvideAuthenticator(config *types.Config, principalStore store.PrincipalStore, tokenStore store.TokenStore) Authenticator {
return NewTokenAuthenticator(principalStore, tokenStore, config.Token.CookieName)
}

View File

@ -119,7 +119,7 @@ func NewAPIHandler(
r.Use(middlewareauthn.Attempt(authenticator, authn.SourceRouterAPI))
r.Route("/v1", func(r chi.Router) {
setupRoutesV1(r, repoCtrl, executionCtrl, triggerCtrl, logCtrl, pipelineCtrl,
setupRoutesV1(r, config, repoCtrl, executionCtrl, triggerCtrl, logCtrl, pipelineCtrl,
connectorCtrl, templateCtrl, pluginCtrl, secretCtrl, spaceCtrl, pullreqCtrl,
webhookCtrl, githookCtrl, saCtrl, userCtrl, principalCtrl, checkCtrl, sysCtrl)
})
@ -142,6 +142,7 @@ func corsHandler(config *types.Config) func(http.Handler) http.Handler {
}
func setupRoutesV1(r chi.Router,
config *types.Config,
repoCtrl *repo.Controller,
executionCtrl *execution.Controller,
triggerCtrl *trigger.Controller,
@ -171,7 +172,7 @@ func setupRoutesV1(r chi.Router,
setupPrincipals(r, principalCtrl)
setupInternal(r, githookCtrl)
setupAdmin(r, userCtrl)
setupAccount(r, userCtrl, sysCtrl)
setupAccount(r, userCtrl, sysCtrl, config)
setupSystem(r, sysCtrl)
setupResources(r)
setupPlugins(r, pluginCtrl)
@ -599,8 +600,9 @@ func setupAdmin(r chi.Router, userCtrl *user.Controller) {
})
}
func setupAccount(r chi.Router, userCtrl *user.Controller, sysCtrl *system.Controller) {
r.Post("/login", account.HandleLogin(userCtrl))
r.Post("/register", account.HandleRegister(userCtrl, sysCtrl))
r.Post("/logout", account.HandleLogout(userCtrl))
func setupAccount(r chi.Router, userCtrl *user.Controller, sysCtrl *system.Controller, config *types.Config) {
cookieName := config.Token.CookieName
r.Post("/login", account.HandleLogin(userCtrl, cookieName))
r.Post("/register", account.HandleRegister(userCtrl, sysCtrl, cookieName))
r.Post("/logout", account.HandleLogout(userCtrl, cookieName))
}

View File

@ -102,6 +102,7 @@ type Config struct {
// Token defines token configuration parameters.
Token struct {
CookieName string `envconfig:"GITNESS_TOKEN_COOKIE_NAME" default:"token"`
Expire time.Duration `envconfig:"GITNESS_TOKEN_EXPIRE" default:"720h"`
}