Merge pull request #201 from ClusterCockpit/hotfix

Hotfix
This commit is contained in:
Jan Eitzinger 2023-08-18 16:01:04 +02:00 committed by GitHub
commit 987f77170f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 41 additions and 40 deletions

View File

@ -16,6 +16,9 @@ The most important routines in auth are:
The http router calls auth in the following cases: The http router calls auth in the following cases:
* `r.Handle("/login", authentication.Login( ... )).Methods(http.MethodPost)`: * `r.Handle("/login", authentication.Login( ... )).Methods(http.MethodPost)`:
The POST request on the `/login` route will call the Login callback. The POST request on the `/login` route will call the Login callback.
* `r.Handle("/jwt-login", authentication.Login( ... ))`:
Any request on the `/jwt-login` route will call the Login callback. Intended
for use for the JWT token based authenticators.
* Any route in the secured subrouter will always call Auth(), on success it will * Any route in the secured subrouter will always call Auth(), on success it will
call the next handler in the chain, on failure it will render the login call the next handler in the chain, on failure it will render the login
template. template.
@ -50,7 +53,6 @@ The Login function (located in `auth.go`):
object is returned. object is returned.
- Creates a new session object, stores the user attributes in the session and - Creates a new session object, stores the user attributes in the session and
saves the session. saves the session.
- If the user does not yet exist in the database try to add the user
- Starts the `onSuccess` http handler - Starts the `onSuccess` http handler
## Local authenticator ## Local authenticator
@ -71,11 +73,20 @@ if e := bcrypt.CompareHashAndPassword([]byte(user.Password), []byte(r.FormValue(
## LDAP authenticator ## LDAP authenticator
This authenticator is applied if This authenticator is applied if the user was found in the database and its
AuthSource is LDAP:
``` ```
return user != nil && user.AuthSource == AuthViaLDAP if user != nil {
if user.AuthSource == schema.AuthViaLDAP {
return user, true
}
}
``` ```
If the option `SyncUserOnLogin` is set it tried to sync the user from the LDAP
directory. In case this succeeds the user is persisted to the database and can
login.
Gets the LDAP connection and tries a bind with the provided credentials: Gets the LDAP connection and tries a bind with the provided credentials:
``` ```
if err := l.Bind(userDn, r.FormValue("password")); err != nil { if err := l.Bind(userDn, r.FormValue("password")); err != nil {
@ -88,33 +99,34 @@ if err := l.Bind(userDn, r.FormValue("password")); err != nil {
Login via JWT token will create a session without password. Login via JWT token will create a session without password.
For login the `X-Auth-Token` header is not supported. This authenticator is For login the `X-Auth-Token` header is not supported. This authenticator is
applied if the Authorization header is present: applied if the Authorization header or query parameter login-token is present:
``` ```
return r.Header.Get("Authorization") != "" return user, r.Header.Get("Authorization") != "" ||
r.URL.Query().Get("login-token") != ""
``` ```
The Login function: The Login function:
* Parses the token * Parses the token and checks if it is expired
* Check if the signing method is EdDSA or HS256 or HS512 * Check if the signing method is EdDSA or HS256 or HS512
* Check if claims are valid and extracts the claims * Check if claims are valid and extracts the claims
* The following claims have to be present: * The following claims have to be present:
- `sub`: The subject, in this case this is the username - `sub`: The subject, in this case this is the username
- `exp`: Expiration in Unix epoch time - `exp`: Expiration in Unix epoch time
- `roles`: String array with roles of user - `roles`: String array with roles of user
* In case user is not yet set, which is usually the case: * In case user does not exist in the database and the option `SyncUserOnLogin`
- Try to fetch user from database is set add user to user database table with `AuthViaToken` AuthSource.
- In case user is not yet present add user to user database table with `AuthViaToken` AuthSource.
* Return valid user object * Return valid user object
## JWT Cookie Session authenticator ## JWT Cookie Session authenticator
Login via JWT cookie token will create a session without password. Login via JWT cookie token will create a session without password.
It is first checked if the required configuration keys are set: It is first checked if the required configuration options are set:
* `publicKeyCrossLogin` * `trustedIssuer`
* `TrustedExternalIssuer`
* `CookieName` * `CookieName`
This authenticator is applied if the configured cookie is present: and optionally the environment variable `CROSS_LOGIN_JWT_PUBLIC_KEY` is set.
This authenticator is applied if the configured cookie is present:
``` ```
jwtCookie, err := r.Cookie(cookieName) jwtCookie, err := r.Cookie(cookieName)
@ -131,9 +143,11 @@ The Login function:
- Return public cross login key - Return public cross login key
- Otherwise return standard public key - Otherwise return standard public key
* Check if claims are valid * Check if claims are valid
* Depending on the option `ForceJWTValidationViaDatabase ` the roles are * Depending on the option `validateUser` the roles are
extracted from JWT token or taken from user object fetched from database extracted from JWT token or taken from user object fetched from database
* Ask browser to delete the JWT cookie * Ask browser to delete the JWT cookie
* In case user does not exist in the database and the option `SyncUserOnLogin`
is set add user to user database table with `AuthViaToken` AuthSource.
* Return valid user object * Return valid user object
# Auth # Auth
@ -154,7 +168,7 @@ Implemented in JWTAuthenticator:
prefix prefix
* Parse token and check if it is valid. The Parse routine will also check if the * Parse token and check if it is valid. The Parse routine will also check if the
token is expired. token is expired.
* If the option `ForceJWTValidationViaDatabase` is set it will ensure the * If the option `validateUser` is set it will ensure the
user object exists in the database and takes the roles from the database user user object exists in the database and takes the roles from the database user
* Otherwise the roles are extracted from the roles claim * Otherwise the roles are extracted from the roles claim
* Returns a valid user object with AuthType set to AuthToken * Returns a valid user object with AuthType set to AuthToken

View File

@ -48,26 +48,8 @@ func (auth *Authentication) AuthViaSession(
if session.IsNew { if session.IsNew {
return nil, nil return nil, nil
} }
//
// var username string // TODO: Check if session keys exist
// var projects, roles []string
//
// if val, ok := session.Values["username"]; ok {
// username, _ = val.(string)
// } else {
// return nil, errors.New("no key username in session")
// }
// if val, ok := session.Values["projects"]; ok {
// projects, _ = val.([]string)
// } else {
// return nil, errors.New("no key projects in session")
// }
// if val, ok := session.Values["projects"]; ok {
// roles, _ = val.([]string)
// } else {
// return nil, errors.New("no key roles in session")
// }
//
username, _ := session.Values["username"].(string) username, _ := session.Values["username"].(string)
projects, _ := session.Values["projects"].([]string) projects, _ := session.Values["projects"].([]string)
roles, _ := session.Values["roles"].([]string) roles, _ := session.Values["roles"].([]string)

View File

@ -228,8 +228,6 @@ func (la *LdapAuthenticator) Sync() error {
return nil return nil
} }
// TODO: Add a connection pool or something like
// that so that connections can be reused/cached.
func (la *LdapAuthenticator) getLdapConnection(admin bool) (*ldap.Conn, error) { func (la *LdapAuthenticator) getLdapConnection(admin bool) (*ldap.Conn, error) {
lc := config.Keys.LdapConfig lc := config.Keys.LdapConfig

View File

@ -154,11 +154,18 @@
? statisticsSeries.mean.length ? statisticsSeries.mean.length
: series.reduce((n, series) => Math.max(n, series.data.length), 0) : series.reduce((n, series) => Math.max(n, series.data.length), 0)
const maxX = longestSeries * timestep const maxX = longestSeries * timestep
const maxY = thresholds != null let maxY = null
? useStatsSeries
if (thresholds !== null) {
maxY = useStatsSeries
? (statisticsSeries.max.reduce((max, x) => Math.max(max, x), thresholds.normal) || thresholds.normal) ? (statisticsSeries.max.reduce((max, x) => Math.max(max, x), thresholds.normal) || thresholds.normal)
: (series.reduce((max, series) => Math.max(max, series.statistics?.max), thresholds.normal) || thresholds.normal) : (series.reduce((max, series) => Math.max(max, series.statistics?.max), thresholds.normal) || thresholds.normal)
: null
if (maxY >= (10 * thresholds.normal)) { // Hard y-range render limit if outliers in series data
maxY = (10 * thresholds.normal)
}
}
const plotSeries = [{label: 'Runtime', value: (u, ts, sidx, didx) => didx == null ? null : formatTime(ts)}] const plotSeries = [{label: 'Runtime', value: (u, ts, sidx, didx) => didx == null ? null : formatTime(ts)}]
const plotData = [new Array(longestSeries)] const plotData = [new Array(longestSeries)]