Improve auth handling of rest apis used in frontend for compatibility

This commit is contained in:
Christoph Kluge 2024-07-04 11:16:45 +02:00
parent 61eebc9fbd
commit 1072d7b449
5 changed files with 96 additions and 13 deletions

View File

@ -373,6 +373,8 @@ func main() {
secured := r.PathPrefix("/").Subrouter() secured := r.PathPrefix("/").Subrouter()
securedapi := r.PathPrefix("/api").Subrouter() securedapi := r.PathPrefix("/api").Subrouter()
userapi := r.PathPrefix("/userapi").Subrouter() userapi := r.PathPrefix("/userapi").Subrouter()
configapi := r.PathPrefix("/config").Subrouter()
userconfigapi := r.PathPrefix("/userconfig").Subrouter()
if !config.Keys.DisableAuthentication { if !config.Keys.DisableAuthentication {
r.Handle("/login", authentication.Login( r.Handle("/login", authentication.Login(
@ -475,6 +477,42 @@ func main() {
}) })
}) })
}) })
configapi.Use(func(next http.Handler) http.Handler {
return authentication.AuthConfigApi(
// On success;
next,
// On failure:
func(rw http.ResponseWriter, r *http.Request, err error) {
rw.WriteHeader(http.StatusUnauthorized)
web.RenderTemplate(rw, "login.tmpl", &web.Page{
Title: "Authentication failed - ClusterCockpit",
MsgType: "alert-danger",
Message: err.Error(),
Build: buildInfo,
Infos: info,
})
})
})
userconfigapi.Use(func(next http.Handler) http.Handler {
return authentication.AuthUserConfigApi(
// On success;
next,
// On failure:
func(rw http.ResponseWriter, r *http.Request, err error) {
rw.WriteHeader(http.StatusUnauthorized)
web.RenderTemplate(rw, "login.tmpl", &web.Page{
Title: "Authentication failed - ClusterCockpit",
MsgType: "alert-danger",
Message: err.Error(),
Build: buildInfo,
Infos: info,
})
})
})
} }
if flagDev { if flagDev {
@ -491,9 +529,10 @@ func main() {
// Mount all /monitoring/... and /api/... routes. // Mount all /monitoring/... and /api/... routes.
routerConfig.SetupRoutes(secured, buildInfo) routerConfig.SetupRoutes(secured, buildInfo)
api.MountConfigApiRoutes(secured)
api.MountApiRoutes(securedapi) api.MountApiRoutes(securedapi)
api.MountUserApiRoutes(userapi) api.MountUserApiRoutes(userapi)
api.MountConfigApiRoutes(configapi)
api.MountUserConfigApiRoutes(userconfigapi)
if config.Keys.EmbedStaticFiles { if config.Keys.EmbedStaticFiles {
if i, err := os.Stat("./var/img"); err == nil { if i, err := os.Stat("./var/img"); err == nil {

View File

@ -92,23 +92,25 @@ func (api *RestApi) MountUserApiRoutes(r *mux.Router) {
r.HandleFunc("/jobs/{id}", api.getJobById).Methods(http.MethodPost) r.HandleFunc("/jobs/{id}", api.getJobById).Methods(http.MethodPost)
r.HandleFunc("/jobs/{id}", api.getCompleteJobById).Methods(http.MethodGet) r.HandleFunc("/jobs/{id}", api.getCompleteJobById).Methods(http.MethodGet)
r.HandleFunc("/jobs/metrics/{id}", api.getJobMetrics).Methods(http.MethodGet) r.HandleFunc("/jobs/metrics/{id}", api.getJobMetrics).Methods(http.MethodGet)
if api.Authentication != nil {
r.HandleFunc("/jwt/", api.getJWT).Methods(http.MethodGet)
}
} }
func (api *RestApi) MountConfigApiRoutes(r *mux.Router) { func (api *RestApi) MountConfigApiRoutes(r *mux.Router) {
r = r.PathPrefix("/config").Subrouter()
r.StrictSlash(true) r.StrictSlash(true)
if api.Authentication != nil { if api.Authentication != nil {
r.HandleFunc("/jwt/", api.getJWT).Methods(http.MethodGet)
r.HandleFunc("/roles/", api.getRoles).Methods(http.MethodGet) r.HandleFunc("/roles/", api.getRoles).Methods(http.MethodGet)
r.HandleFunc("/users/", api.createUser).Methods(http.MethodPost, http.MethodPut) r.HandleFunc("/users/", api.createUser).Methods(http.MethodPost, http.MethodPut)
r.HandleFunc("/users/", api.getUsers).Methods(http.MethodGet) r.HandleFunc("/users/", api.getUsers).Methods(http.MethodGet)
r.HandleFunc("/users/", api.deleteUser).Methods(http.MethodDelete) r.HandleFunc("/users/", api.deleteUser).Methods(http.MethodDelete)
r.HandleFunc("/user/{id}", api.updateUser).Methods(http.MethodPost) r.HandleFunc("/user/{id}", api.updateUser).Methods(http.MethodPost)
}
}
func (api *RestApi) MountUserConfigApiRoutes(r *mux.Router) {
r.StrictSlash(true)
if api.Authentication != nil {
r.HandleFunc("/jwt/", api.getJWT).Methods(http.MethodGet) // Role:Admin Check in
r.HandleFunc("/configuration/", api.updateConfiguration).Methods(http.MethodPost) r.HandleFunc("/configuration/", api.updateConfiguration).Methods(http.MethodPost)
} }
} }

View File

@ -314,6 +314,48 @@ func (auth *Authentication) AuthUserApi(
}) })
} }
func (auth *Authentication) AuthConfigApi(
onsuccess http.Handler,
onfailure func(rw http.ResponseWriter, r *http.Request, authErr error),
) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
user, err := auth.AuthViaSession(rw, r)
if err != nil {
log.Infof("authentication failed: %s", err.Error())
http.Error(rw, err.Error(), http.StatusUnauthorized)
return
}
if user != nil && user.HasRole(schema.RoleAdmin) {
ctx := context.WithValue(r.Context(), repository.ContextUserKey, user)
onsuccess.ServeHTTP(rw, r.WithContext(ctx))
return
}
log.Debug("authentication failed")
onfailure(rw, r, errors.New("unauthorized (no auth)"))
})
}
func (auth *Authentication) AuthUserConfigApi(
onsuccess http.Handler,
onfailure func(rw http.ResponseWriter, r *http.Request, authErr error),
) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
user, err := auth.AuthViaSession(rw, r)
if err != nil {
log.Infof("authentication failed: %s", err.Error())
http.Error(rw, err.Error(), http.StatusUnauthorized)
return
}
if user != nil {
ctx := context.WithValue(r.Context(), repository.ContextUserKey, user)
onsuccess.ServeHTTP(rw, r.WithContext(ctx))
return
}
log.Debug("authentication failed")
onfailure(rw, r, errors.New("unauthorized (no auth)"))
})
}
func (auth *Authentication) Logout(onsuccess http.Handler) http.Handler { func (auth *Authentication) Logout(onsuccess http.Handler) http.Handler {
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) { return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
session, err := auth.sessionStore.Get(r, "session") session, err := auth.sessionStore.Get(r, "session")

View File

@ -283,7 +283,7 @@
<form <form
id="line-width-form" id="line-width-form"
method="post" method="post"
action="/config/configuration/" action="/userconfig//configuration/"
class="card-body" class="card-body"
on:submit|preventDefault={() => on:submit|preventDefault={() =>
handleSettingSubmit("#line-width-form", "lw")} handleSettingSubmit("#line-width-form", "lw")}
@ -329,7 +329,7 @@
<form <form
id="plots-per-row-form" id="plots-per-row-form"
method="post" method="post"
action="/config/configuration/" action="/userconfig/configuration/"
class="card-body" class="card-body"
on:submit|preventDefault={() => on:submit|preventDefault={() =>
handleSettingSubmit("#plots-per-row-form", "ppr")} handleSettingSubmit("#plots-per-row-form", "ppr")}
@ -375,7 +375,7 @@
<form <form
id="backgrounds-form" id="backgrounds-form"
method="post" method="post"
action="/config/configuration/" action="/userconfig/configuration/"
class="card-body" class="card-body"
on:submit|preventDefault={() => on:submit|preventDefault={() =>
handleSettingSubmit("#backgrounds-form", "bg")} handleSettingSubmit("#backgrounds-form", "bg")}
@ -429,7 +429,7 @@
<form <form
id="paging-form" id="paging-form"
method="post" method="post"
action="/config/configuration/" action="/userconfig/configuration/"
class="card-body" class="card-body"
on:submit|preventDefault={() => on:submit|preventDefault={() =>
handleSettingSubmit("#paging-form", "pag")} handleSettingSubmit("#paging-form", "pag")}
@ -485,7 +485,7 @@
<form <form
id="colorscheme-form" id="colorscheme-form"
method="post" method="post"
action="/config/configuration/" action="/userconfig/configuration/"
class="card-body" class="card-body"
> >
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. --> <!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->

View File

@ -5,7 +5,7 @@
let jwt = ""; let jwt = "";
function getUserJwt(username) { function getUserJwt(username) {
fetch(`/config/jwt/?username=${username}`) fetch(`/userconfig/jwt/?username=${username}`)
.then((res) => res.text()) .then((res) => res.text())
.then((text) => { .then((text) => {
jwt = text; jwt = text;