Skip to content

Commit

Permalink
rename api userconfig to frontend, return json on api auth error
Browse files Browse the repository at this point in the history
  • Loading branch information
spacehamster87 committed Jul 5, 2024
1 parent 9d47675 commit 3afe400
Show file tree
Hide file tree
Showing 8 changed files with 80 additions and 69 deletions.
56 changes: 24 additions & 32 deletions cmd/cc-backend/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down Expand Up @@ -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(),
})
})
})
Expand All @@ -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(),
})
})
})
Expand All @@ -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(),
})
})
})
Expand All @@ -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 {
Expand Down
5 changes: 3 additions & 2 deletions internal/api/rest.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
}
}

Expand Down
50 changes: 24 additions & 26 deletions internal/auth/auth.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)"))
})
}
Expand All @@ -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 {
Expand All @@ -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"))
})
}

Expand All @@ -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 {
Expand All @@ -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"))
})
}

Expand All @@ -321,38 +319,38 @@ 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) {
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)"))
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 {
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)"))
log.Info("auth frontend api -> authentication failed: no auth")
onfailure(rw, r, errors.New("unauthorized"))
})
}

Expand Down
24 changes: 22 additions & 2 deletions internal/repository/job.go
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion web/frontend/src/config/user/PlotColorScheme.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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. -->
Expand Down
6 changes: 3 additions & 3 deletions web/frontend/src/config/user/PlotRenderOptions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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")}
Expand Down Expand Up @@ -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")}
Expand Down Expand Up @@ -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")}
Expand Down
2 changes: 1 addition & 1 deletion web/frontend/src/config/user/UserOptions.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -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")}
Expand Down
4 changes: 2 additions & 2 deletions web/frontend/src/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -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(
"&"
)}`
);
Expand Down Expand Up @@ -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}`;
Expand Down

0 comments on commit 3afe400

Please sign in to comment.