Cyclops 4 HPC is the purpose built stack to support large HPC centers with resource accounting and billing of cluster as well as cloud resources.
 
 
cyclops-4-hpc/services/billing/server/security.go

232 lines
5.2 KiB

package main
import (
"context"
"errors"
"strconv"
"github.com/Nerzal/gocloak/v7"
"github.com/prometheus/client_golang/prometheus"
l "gitlab.com/cyclops-utilities/logging"
)
type basicUserData struct {
UserSub string
}
type userInfo struct {
Username string `json:"username"`
Firstname string `json:"firstname"`
Lastname string `json:"lastname"`
EmailAddress string `json:"email"`
EmailVerified bool `json:"emailverified"`
ID string `json:"id"`
}
// AuthAPIKey (Swagger func) assures that the token provided when connecting to
// the API is the one presented in the config file as the valid token for the
// deployment.
func AuthAPIKey(token string) (i interface{}, e error) {
l.Warning.Printf("[SECURITY] APIKey Authentication: Trying entry with token: %v\n", token)
if !cfg.APIKey.Enabled {
e = errors.New("the API Key authentication is not active in this deployment")
metricSecurity.With(prometheus.Labels{"mode": "APIKey", "state": "DISABLED"}).Inc()
return
}
if cfg.APIKey.Token == token {
i = basicUserData{
UserSub: token,
}
metricSecurity.With(prometheus.Labels{"mode": "APIKey", "state": "ACCESS GRANTED"}).Inc()
} else {
e = errors.New("provided token is not valid")
metricSecurity.With(prometheus.Labels{"mode": "APIKey", "state": "INVALID TOKEN"}).Inc()
}
return
}
// AuthKeycloak (Swagger func) is called whenever it is necessary to check if a
// token which is presented to access an API is valid.
// The functions assumes that in keycloak the roles are going to be in uppercase
// and in the form of SCOPE_ROLE.
// Example:
// ADMIN_ROLE <-> scope admin
func AuthKeycloak(token string, scopes []string) (i interface{}, returnErr error) {
l.Debug.Printf("[SECURITY] AuthKeycloak: Performing authentication check - token = %v...%v\n", token, scopes)
if !cfg.Keycloak.Enabled {
returnErr = errors.New("the Keycloak authentication is not active in this deployment")
metricSecurity.With(prometheus.Labels{"mode": "Keycloak", "state": "DISABLED"}).Inc()
return
}
keycloakService := getKeycloakService(cfg.Keycloak)
client := gocloak.NewClient(keycloakService)
ctx := context.Background()
_, err := client.LoginClient(ctx, cfg.Keycloak.ClientID, cfg.Keycloak.ClientSecret, cfg.Keycloak.Realm)
// clientToken, err := client.LoginClient(ctx, cfg.Keycloak.ClientID, cfg.Keycloak.ClientSecret, cfg.Keycloak.Realm)
if err != nil {
l.Warning.Printf("[SECURITY] Problems logging in to keycloak. Error: %v\n", err.Error())
returnErr = errors.New("unable to log in to keycloak")
metricSecurity.With(prometheus.Labels{"mode": "Keycloak", "state": "FAIL: Keycloak Server unavailable"}).Inc()
return
}
u, err := client.GetUserInfo(ctx, token, cfg.Keycloak.Realm)
if err != nil {
l.Warning.Printf("[SECURITY] Problems getting user Info. Error: %v\n", err.Error())
returnErr = errors.New("unable to get userInfo from keycloak")
metricSecurity.With(prometheus.Labels{"mode": "Keycloak", "state": "FAIL: Keycloak Server unavailable userInfo"}).Inc()
return
}
if u != nil {
i = basicUserData{
UserSub: *u.Sub,
}
metricSecurity.With(prometheus.Labels{"mode": "Keycloak", "state": "ACCESS GRANTED"}).Inc()
}
// userRoles := make(map[string]bool)
//
// roles, err := client.GetRoleMappingByUserID(ctx, clientToken.AccessToken, cfg.Keycloak.Realm, *u.Sub)
//
// if err != nil {
//
// l.Warning.Printf("[SECURITY] Problems getting roles by user ID. Error:\n" + err.Error())
//
// returnErr = errors.New("unable to retrieve user roles from keycloak")
//
// return
//
// }
//
// for _, m := range roles.RealmMappings {
//
// userRoles[*m.Name] = true
//
// }
//
// ug, err := client.GetUserGroups(ctx, clientToken.AccessToken, cfg.Keycloak.Realm, *u.Sub)
//
// if err != nil {
//
// l.Warning.Printf("[SECURITY] Problems getting groups by user ID. Error:\n" + err.Error())
//
// returnErr = errors.New("unable to get user groups from keycloak")
//
// return
//
// }
//
// for _, m := range ug {
//
// roles, err := client.GetRoleMappingByGroupID(ctx, clientToken.AccessToken, cfg.Keycloak.Realm, *m.ID)
//
// if err != nil {
//
// l.Warning.Printf("[SECURITY] Problems getting roles by group ID. Error:\n" + err.Error())
//
// returnErr = errors.New("unable get groups roles from keycloak")
//
// return
//
// }
//
// for _, n := range roles.RealmMappings {
//
// userRoles[*n.Name] = true
//
// }
//
// }
//
// control := false
//
// for _, sc := range scopes {
//
// if userRoles[strings.ToUpper(sc)+"_ROLE"] {
//
// control = true
//
// }
//
// }
//
// if !control {
//
// returnErr = errors2.New(401, "The required role is not present in the user permissions")
//
// return
//
// }
return
}
// getKeycloaktService returns the keycloak service; note that there has to be exceptional
// handling of port 80 and port 443
func getKeycloakService(c keycloakConfig) (s string) {
if c.UseHTTP {
s = "http://" + c.Host
if c.Port != 80 {
s = s + ":" + strconv.Itoa(c.Port)
}
} else {
s = "https://" + c.Host
if c.Port != 443 {
s = s + ":" + strconv.Itoa(c.Port)
}
}
return
}