rename api userconfig to frontend, return json on api auth error

This commit is contained in:
Christoph Kluge 2024-07-05 11:48:06 +02:00
parent 9d4767539c
commit 3afe40083d
8 changed files with 80 additions and 69 deletions

View File

@ -374,7 +374,7 @@ func main() {
securedapi := r.PathPrefix("/api").Subrouter()
userapi := r.PathPrefix("/userapi").Subrouter()
configapi := r.PathPrefix("/config").Subrouter()
userconfigapi := r.PathPrefix("/userconfig").Subrouter()
frontendapi := r.PathPrefix("/frontend").Subrouter()
if !config.Keys.DisableAuthentication {
r.Handle("/login", authentication.Login(
@ -447,15 +447,13 @@ func main() {
// On success;
next,
// On failure:
// On failure: JSON Response
func(rw http.ResponseWriter, r *http.Request, err error) {
rw.Header().Add("Content-Type", "application/json")
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,
json.NewEncoder(rw).Encode(map[string]string{
"status": http.StatusText(http.StatusUnauthorized),
"error": err.Error(),
})
})
})
@ -465,15 +463,13 @@ func main() {
// On success;
next,
// On failure:
// On failure: JSON Response
func(rw http.ResponseWriter, r *http.Request, err error) {
rw.Header().Add("Content-Type", "application/json")
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,
json.NewEncoder(rw).Encode(map[string]string{
"status": http.StatusText(http.StatusUnauthorized),
"error": err.Error(),
})
})
})
@ -483,33 +479,29 @@ func main() {
// On success;
next,
// On failure:
// On failure: JSON Response
func(rw http.ResponseWriter, r *http.Request, err error) {
rw.Header().Add("Content-Type", "application/json")
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,
json.NewEncoder(rw).Encode(map[string]string{
"status": http.StatusText(http.StatusUnauthorized),
"error": err.Error(),
})
})
})
userconfigapi.Use(func(next http.Handler) http.Handler {
return authentication.AuthUserConfigApi(
frontendapi.Use(func(next http.Handler) http.Handler {
return authentication.AuthFrontendApi(
// On success;
next,
// On failure:
// On failure: JSON Response
func(rw http.ResponseWriter, r *http.Request, err error) {
rw.Header().Add("Content-Type", "application/json")
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,
json.NewEncoder(rw).Encode(map[string]string{
"status": http.StatusText(http.StatusUnauthorized),
"error": err.Error(),
})
})
})
@ -532,7 +524,7 @@ func main() {
api.MountApiRoutes(securedapi)
api.MountUserApiRoutes(userapi)
api.MountConfigApiRoutes(configapi)
api.MountUserConfigApiRoutes(userconfigapi)
api.MountFrontendApiRoutes(frontendapi)
if config.Keys.EmbedStaticFiles {
if i, err := os.Stat("./var/img"); err == nil {

View File

@ -106,12 +106,13 @@ func (api *RestApi) MountConfigApiRoutes(r *mux.Router) {
}
}
func (api *RestApi) MountUserConfigApiRoutes(r *mux.Router) {
func (api *RestApi) MountFrontendApiRoutes(r *mux.Router) {
r.StrictSlash(true)
if api.Authentication != nil {
r.HandleFunc("/jwt/", api.getJWT).Methods(http.MethodGet) // Role:Admin Check in
r.HandleFunc("/jwt/", api.getJWT).Methods(http.MethodGet)
r.HandleFunc("/configuration/", api.updateConfiguration).Methods(http.MethodPost)
r.HandleFunc("/jobs/metrics/{id}", api.getJobMetrics).Methods(http.MethodGet) // Fetched in Job.svelte: Needs All-User-Access-Session-Auth
}
}

View File

@ -219,27 +219,25 @@ func (auth *Authentication) Auth(
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
user, err := auth.JwtAuth.AuthViaJWT(rw, r)
if err != nil {
log.Infof("authentication failed: %s", err.Error())
log.Infof("auth -> authentication failed: %s", err.Error())
http.Error(rw, err.Error(), http.StatusUnauthorized)
return
}
if user == nil {
user, err = auth.AuthViaSession(rw, r)
if err != nil {
log.Infof("authentication failed: %s", err.Error())
log.Infof("auth -> 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")
log.Info("auth -> authentication failed")
onfailure(rw, r, errors.New("unauthorized (please login first)"))
})
}
@ -251,8 +249,8 @@ func (auth *Authentication) AuthApi(
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
user, err := auth.JwtAuth.AuthViaJWT(rw, r)
if err != nil {
log.Infof("authentication failed: %s", err.Error())
http.Error(rw, err.Error(), http.StatusUnauthorized)
log.Infof("auth api -> authentication failed: %s", err.Error())
onfailure(rw, r, err)
return
}
if user != nil {
@ -270,12 +268,12 @@ func (auth *Authentication) AuthApi(
return
}
default:
log.Debug("authentication failed")
onfailure(rw, r, errors.New("unauthorized (missing role)"))
log.Info("auth api -> authentication failed: missing role")
onfailure(rw, r, errors.New("unauthorized"))
}
}
log.Debug("authentication failed")
onfailure(rw, r, errors.New("unauthorized (no auth)"))
log.Info("auth api -> authentication failed: no auth")
onfailure(rw, r, errors.New("unauthorized"))
})
}
@ -286,8 +284,8 @@ func (auth *Authentication) AuthUserApi(
return http.HandlerFunc(func(rw http.ResponseWriter, r *http.Request) {
user, err := auth.JwtAuth.AuthViaJWT(rw, r)
if err != nil {
log.Infof("authentication failed: %s", err.Error())
http.Error(rw, err.Error(), http.StatusUnauthorized)
log.Infof("auth user api -> authentication failed: %s", err.Error())
onfailure(rw, r, err)
return
}
if user != nil {
@ -305,12 +303,12 @@ func (auth *Authentication) AuthUserApi(
return
}
default:
log.Debug("authentication failed")
onfailure(rw, r, errors.New("unauthorized (missing role)"))
log.Info("auth user api -> authentication failed: missing role")
onfailure(rw, r, errors.New("unauthorized"))
}
}
log.Debug("authentication failed")
onfailure(rw, r, errors.New("unauthorized (no auth)"))
log.Info("auth user api -> authentication failed: no auth")
onfailure(rw, r, errors.New("unauthorized"))
})
}
@ -321,8 +319,8 @@ func (auth *Authentication) AuthConfigApi(
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)
log.Infof("auth config api -> authentication failed: %s", err.Error())
onfailure(rw, r, err)
return
}
if user != nil && user.HasRole(schema.RoleAdmin) {
@ -330,20 +328,20 @@ func (auth *Authentication) AuthConfigApi(
onsuccess.ServeHTTP(rw, r.WithContext(ctx))
return
}
log.Debug("authentication failed")
onfailure(rw, r, errors.New("unauthorized (no auth)"))
log.Info("auth config api -> authentication failed: no auth")
onfailure(rw, r, errors.New("unauthorized"))
})
}
func (auth *Authentication) AuthUserConfigApi(
func (auth *Authentication) AuthFrontendApi(
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)
log.Infof("auth frontend api -> authentication failed: %s", err.Error())
onfailure(rw, r, err)
return
}
if user != nil {
@ -351,8 +349,8 @@ func (auth *Authentication) AuthUserConfigApi(
onsuccess.ServeHTTP(rw, r.WithContext(ctx))
return
}
log.Debug("authentication failed")
onfailure(rw, r, errors.New("unauthorized (no auth)"))
log.Info("auth frontend api -> authentication failed: no auth")
onfailure(rw, r, errors.New("unauthorized"))
})
}

View File

@ -305,16 +305,36 @@ func (r *JobRepository) FindByIdDirect(jobId int64) (*schema.Job, error) {
return scanJob(q.RunWith(r.stmtCache).QueryRow())
}
// FindByJobId executes a SQL query to find a specific batch job.
// The job is queried using the slurm id and the clustername.
// It returns a pointer to a schema.Job data structure and an error variable.
// To check if no job was found test err == sql.ErrNoRows
func (r *JobRepository) FindByJobId(ctx context.Context, jobId int64, startTime int64, cluster string) (*schema.Job, error) {
q := sq.Select(jobColumns...).
From("job").
Where("job.job_id = ?", jobId).
Where("job.cluster = ?", cluster).
Where("job.start_time = ?", startTime)
q, qerr := SecurityCheck(ctx, q)
if qerr != nil {
return nil, qerr
}
return scanJob(q.RunWith(r.stmtCache).QueryRow())
}
// IsJobOwner executes a SQL query to find a specific batch job.
// The job is queried using the slurm id,a username and the cluster.
// It returns a bool.
// If job was found, user is owner: test err != sql.ErrNoRows
func (r *JobRepository) IsJobOwner(jobId int64, user string, cluster string) bool {
func (r *JobRepository) IsJobOwner(jobId int64, startTime int64, user string, cluster string) bool {
q := sq.Select("id").
From("job").
Where("job.job_id = ?", jobId).
Where("job.user = ?", user).
Where("job.cluster = ?", cluster)
Where("job.cluster = ?", cluster).
Where("job.start_time = ?", startTime)
_, err := scanJob(q.RunWith(r.stmtCache).QueryRow())
return err != sql.ErrNoRows

View File

@ -262,7 +262,7 @@
<form
id="colorscheme-form"
method="post"
action="/userconfig/configuration/"
action="/frontend/configuration/"
class="card-body"
>
<!-- Svelte 'class' directive only on DOMs directly, normal 'class="xxx"' does not work, so style-array it is. -->

View File

@ -30,7 +30,7 @@
<form
id="line-width-form"
method="post"
action="/userconfig/configuration/"
action="/frontend/configuration/"
class="card-body"
on:submit|preventDefault={() =>
updateSetting("#line-width-form", "lw")}
@ -76,7 +76,7 @@
<form
id="plots-per-row-form"
method="post"
action="/userconfig/configuration/"
action="/frontend/configuration/"
class="card-body"
on:submit|preventDefault={() =>
updateSetting("#plots-per-row-form", "ppr")}
@ -122,7 +122,7 @@
<form
id="backgrounds-form"
method="post"
action="/userconfig/configuration/"
action="/frontend/configuration/"
class="card-body"
on:submit|preventDefault={() =>
updateSetting("#backgrounds-form", "bg")}

View File

@ -51,7 +51,7 @@
<form
id="paging-form"
method="post"
action="/userconfig/configuration/"
action="/frontend/configuration/"
class="card-body"
on:submit|preventDefault={() =>
updateSetting("#paging-form", "pag")}

View File

@ -239,7 +239,7 @@ export async function fetchMetrics(job, metrics, scopes) {
try {
let res = await fetch(
`/api/jobs/metrics/${job.id}${query.length > 0 ? "?" : ""}${query.join(
`/frontend/jobs/metrics/${job.id}${query.length > 0 ? "?" : ""}${query.join(
"&"
)}`
);
@ -434,7 +434,7 @@ export function transformPerNodeDataForRoofline(nodes) {
}
export async function fetchJwt(username) {
const raw = await fetch(`/userconfig/jwt/?username=${username}`);
const raw = await fetch(`/frontend/jwt/?username=${username}`);
if (!raw.ok) {
const message = `An error has occured: ${response.status}`;