Skip to content
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion cmd/new-ui/v1beta1/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ func main() {
http.HandleFunc("/katib/", kuh.ServeIndex(*buildDir))
http.Handle("/katib/static/", http.StripPrefix("/katib/", frontend))

http.HandleFunc("/katib/fetch_experiments/", kuh.FetchAllExperiments)
http.HandleFunc("/katib/fetch_experiments/", kuh.FetchExperiments)

http.HandleFunc("/katib/create_experiment/", kuh.CreateExperiment)

Expand Down
1 change: 1 addition & 0 deletions pkg/new-ui/v1beta1/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ This is the recommended way to test the web app e2e. In order to build the UI an
For example, if you use port-forwarding to expose `katib-db-manager`, run this command:

```
export APP_DISABLE_AUTH=true
go run main.go --build-dir=../../../pkg/new-ui/v1beta1/frontend/dist --port=8080 --db-manager-address=localhost:6789
```

Expand Down
106 changes: 106 additions & 0 deletions pkg/new-ui/v1beta1/authzn.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
package v1beta1

import (
"context"
"errors"
"fmt"
"log"
"net/http"
"strings"

"github.com/kubeflow/katib/pkg/util/v1beta1/env"
v1 "k8s.io/api/authorization/v1"
"k8s.io/apimachinery/pkg/runtime/schema"
"sigs.k8s.io/controller-runtime/pkg/client"
)

// ENV variables
var (
USER_HEADER = env.GetEnvOrDefault("USERID_HEADER", "kubeflow-userid")
USER_PREFIX = env.GetEnvOrDefault("USERID_PREFIX", ":")
DISABLE_AUTH = env.GetEnvOrDefault("APP_DISABLE_AUTH", "false")
)

func GetUsername(r *http.Request) (string, error) {
var username string
if DISABLE_AUTH == "true" {
log.Printf("APP_DISABLE_AUTH set to True. Skipping authentication check")
return "", nil
}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can we avoid duplicate check for if DISABLE_AUTH == "true" by adding GetUsername logic under IsAuthorized call ?
Then, we have 1 place where we verify if auth is required.
WDYT @apo-ger ?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Sure, I moved the GetUsername logic under IsAuthorized and introduced the appropriate checks to handle the various errors properly!


if r.Header.Get(USER_HEADER) == "" {
return "", errors.New("user header not present")
}

user := r.Header.Get(USER_HEADER)
username = strings.Replace(user, USER_PREFIX, "", 1)

return username, nil
}

// Function for constructing SubjectAccessReviews (SAR) objects
func CreateSAR(user, verb, namespace, resource, subresource, name string, schema schema.GroupVersion) *v1.SubjectAccessReview {

sar := &v1.SubjectAccessReview{
Spec: v1.SubjectAccessReviewSpec{
User: user,
ResourceAttributes: &v1.ResourceAttributes{
Namespace: namespace,
Verb: verb,
Group: schema.Group,
Version: schema.Version,
Resource: resource,
Subresource: subresource,
Name: name,
},
},
}
return sar
}

func IsAuthorized(user, verb, namespace, resource, subresource, name string, schema schema.GroupVersion, client client.Client) error {

// Skip authz when admin is explicity requested it
if DISABLE_AUTH == "true" {
log.Printf("APP_DISABLE_AUTH set to True. Skipping authorization check")
return nil
}

sar := CreateSAR(user, verb, namespace, resource, subresource, name, schema)

err := client.Create(context.TODO(), sar)
if err != nil {
log.Printf("Error submitting SubjectAccessReview: %v, %s", sar, err.Error())
return err
}

if sar.Status.Allowed {
return nil
}

msg := generateUnauthorizedMessage(user, verb, namespace, resource, subresource, schema, sar)
return errors.New(msg)
}

func generateUnauthorizedMessage(user, verb, namespace, resource, subresource string, schema schema.GroupVersion, sar *v1.SubjectAccessReview) string {

msg := fmt.Sprintf("User: %s is not authorized to %s", user, verb)

if schema.Group == "" {
msg += fmt.Sprintf(" %s/%s", schema.Version, resource)
} else {
msg += fmt.Sprintf(" %s/%s/%s", schema.Group, schema.Version, resource)
}

if subresource != "" {
msg += fmt.Sprintf("/%s", subresource)
}

if namespace != "" {
msg += fmt.Sprintf(" in namespace: %s", namespace)
}
if sar.Status.Reason != "" {
msg += fmt.Sprintf(" ,reason: %s", sar.Status.Reason)
}
return msg
}
Loading