The websocket client and hub interaction has been simplified a bit. The hub now acts only as a tee writer to the various clients that register. Clients must register and unregister explicitly. The hub is no longer passed in to the client. Websocket clients now watch for password changes or jwt token expiration times. Clients are disconnected if auth token expires or if the password is changed. Various aditional safety checks have been added. Signed-off-by: Gabriel Adrian Samfira <gsamfira@cloudbasesolutions.com>
281 lines
7.7 KiB
Go
281 lines
7.7 KiB
Go
// Copyright 2022 Cloudbase Solutions SRL
|
|
//
|
|
// Licensed under the Apache License, Version 2.0 (the "License"); you may
|
|
// not use this file except in compliance with the License. You may obtain
|
|
// a copy of the License at
|
|
//
|
|
// http://www.apache.org/licenses/LICENSE-2.0
|
|
//
|
|
// Unless required by applicable law or agreed to in writing, software
|
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
|
// License for the specific language governing permissions and limitations
|
|
// under the License.
|
|
|
|
package auth
|
|
|
|
import (
|
|
"context"
|
|
"time"
|
|
|
|
runnerErrors "github.com/cloudbase/garm-provider-common/errors"
|
|
"github.com/cloudbase/garm/params"
|
|
)
|
|
|
|
type contextFlags string
|
|
|
|
const (
|
|
isAdminKey contextFlags = "is_admin"
|
|
fullNameKey contextFlags = "full_name"
|
|
readMetricsKey contextFlags = "read_metrics"
|
|
// UserIDFlag is the User ID flag we set in the context
|
|
UserIDFlag contextFlags = "user_id"
|
|
isEnabledFlag contextFlags = "is_enabled"
|
|
jwtTokenFlag contextFlags = "jwt_token"
|
|
authExpiresFlag contextFlags = "auth_expires"
|
|
passwordGenerationFlag contextFlags = "password_generation"
|
|
|
|
instanceIDKey contextFlags = "id"
|
|
instanceNameKey contextFlags = "name"
|
|
instancePoolIDKey contextFlags = "pool_id"
|
|
instancePoolTypeKey contextFlags = "scope"
|
|
instanceEntityKey contextFlags = "entity"
|
|
instanceRunnerStatus contextFlags = "status"
|
|
instanceTokenFetched contextFlags = "tokenFetched"
|
|
instanceHasJITConfig contextFlags = "hasJITConfig"
|
|
instanceParams contextFlags = "instanceParams"
|
|
)
|
|
|
|
func SetInstanceID(ctx context.Context, id string) context.Context {
|
|
return context.WithValue(ctx, instanceIDKey, id)
|
|
}
|
|
|
|
func InstanceID(ctx context.Context) string {
|
|
elem := ctx.Value(instanceIDKey)
|
|
if elem == nil {
|
|
return ""
|
|
}
|
|
return elem.(string)
|
|
}
|
|
|
|
func SetInstanceTokenFetched(ctx context.Context, fetched bool) context.Context {
|
|
return context.WithValue(ctx, instanceTokenFetched, fetched)
|
|
}
|
|
|
|
func InstanceTokenFetched(ctx context.Context) bool {
|
|
elem := ctx.Value(instanceTokenFetched)
|
|
if elem == nil {
|
|
return false
|
|
}
|
|
return elem.(bool)
|
|
}
|
|
|
|
func SetInstanceHasJITConfig(ctx context.Context, cfg map[string]string) context.Context {
|
|
return context.WithValue(ctx, instanceHasJITConfig, len(cfg) > 0)
|
|
}
|
|
|
|
func InstanceHasJITConfig(ctx context.Context) bool {
|
|
elem := ctx.Value(instanceHasJITConfig)
|
|
if elem == nil {
|
|
return false
|
|
}
|
|
return elem.(bool)
|
|
}
|
|
|
|
func SetInstanceParams(ctx context.Context, instance params.Instance) context.Context {
|
|
return context.WithValue(ctx, instanceParams, instance)
|
|
}
|
|
|
|
func InstanceParams(ctx context.Context) (params.Instance, error) {
|
|
elem := ctx.Value(instanceParams)
|
|
if elem == nil {
|
|
return params.Instance{}, runnerErrors.ErrNotFound
|
|
}
|
|
|
|
instanceParams, ok := elem.(params.Instance)
|
|
if !ok {
|
|
return params.Instance{}, runnerErrors.ErrNotFound
|
|
}
|
|
return instanceParams, nil
|
|
}
|
|
|
|
func SetInstanceRunnerStatus(ctx context.Context, val params.RunnerStatus) context.Context {
|
|
return context.WithValue(ctx, instanceRunnerStatus, val)
|
|
}
|
|
|
|
func InstanceRunnerStatus(ctx context.Context) params.RunnerStatus {
|
|
elem := ctx.Value(instanceRunnerStatus)
|
|
if elem == nil {
|
|
return params.RunnerPending
|
|
}
|
|
return elem.(params.RunnerStatus)
|
|
}
|
|
|
|
func SetInstanceName(ctx context.Context, val string) context.Context {
|
|
return context.WithValue(ctx, instanceNameKey, val)
|
|
}
|
|
|
|
func InstanceName(ctx context.Context) string {
|
|
elem := ctx.Value(instanceNameKey)
|
|
if elem == nil {
|
|
return ""
|
|
}
|
|
return elem.(string)
|
|
}
|
|
|
|
func SetInstancePoolID(ctx context.Context, val string) context.Context {
|
|
return context.WithValue(ctx, instancePoolIDKey, val)
|
|
}
|
|
|
|
func InstancePoolID(ctx context.Context) string {
|
|
elem := ctx.Value(instancePoolIDKey)
|
|
if elem == nil {
|
|
return ""
|
|
}
|
|
return elem.(string)
|
|
}
|
|
|
|
func SetInstancePoolType(ctx context.Context, val string) context.Context {
|
|
return context.WithValue(ctx, instancePoolTypeKey, val)
|
|
}
|
|
|
|
func InstancePoolType(ctx context.Context) string {
|
|
elem := ctx.Value(instancePoolTypeKey)
|
|
if elem == nil {
|
|
return ""
|
|
}
|
|
return elem.(string)
|
|
}
|
|
|
|
func SetInstanceEntity(ctx context.Context, val string) context.Context {
|
|
return context.WithValue(ctx, instanceEntityKey, val)
|
|
}
|
|
|
|
func InstanceEntity(ctx context.Context) string {
|
|
elem := ctx.Value(instanceEntityKey)
|
|
if elem == nil {
|
|
return ""
|
|
}
|
|
return elem.(string)
|
|
}
|
|
|
|
func PopulateInstanceContext(ctx context.Context, instance params.Instance) context.Context {
|
|
ctx = SetInstanceID(ctx, instance.ID)
|
|
ctx = SetInstanceName(ctx, instance.Name)
|
|
ctx = SetInstancePoolID(ctx, instance.PoolID)
|
|
ctx = SetInstanceRunnerStatus(ctx, instance.RunnerStatus)
|
|
ctx = SetInstanceTokenFetched(ctx, instance.TokenFetched)
|
|
ctx = SetInstanceHasJITConfig(ctx, instance.JitConfiguration)
|
|
ctx = SetInstanceParams(ctx, instance)
|
|
return ctx
|
|
}
|
|
|
|
// PopulateContext sets the appropriate fields in the context, based on
|
|
// the user object
|
|
func PopulateContext(ctx context.Context, user params.User, authExpires *time.Time) context.Context {
|
|
ctx = SetUserID(ctx, user.ID)
|
|
ctx = SetAdmin(ctx, user.IsAdmin)
|
|
ctx = SetIsEnabled(ctx, user.Enabled)
|
|
ctx = SetFullName(ctx, user.FullName)
|
|
ctx = SetExpires(ctx, authExpires)
|
|
ctx = SetPasswordGeneration(ctx, user.Generation)
|
|
return ctx
|
|
}
|
|
|
|
func SetExpires(ctx context.Context, expires *time.Time) context.Context {
|
|
if expires == nil {
|
|
return ctx
|
|
}
|
|
return context.WithValue(ctx, authExpiresFlag, expires)
|
|
}
|
|
|
|
func Expires(ctx context.Context) *time.Time {
|
|
elem := ctx.Value(authExpiresFlag)
|
|
if elem == nil {
|
|
return nil
|
|
}
|
|
return elem.(*time.Time)
|
|
}
|
|
|
|
func SetPasswordGeneration(ctx context.Context, val uint) context.Context {
|
|
return context.WithValue(ctx, passwordGenerationFlag, val)
|
|
}
|
|
|
|
func PasswordGeneration(ctx context.Context) uint {
|
|
elem := ctx.Value(passwordGenerationFlag)
|
|
if elem == nil {
|
|
return 0
|
|
}
|
|
return elem.(uint)
|
|
}
|
|
|
|
// SetFullName sets the user full name in the context
|
|
func SetFullName(ctx context.Context, fullName string) context.Context {
|
|
return context.WithValue(ctx, fullNameKey, fullName)
|
|
}
|
|
|
|
// FullName returns the full name from context
|
|
func FullName(ctx context.Context) string {
|
|
name := ctx.Value(fullNameKey)
|
|
if name == nil {
|
|
return ""
|
|
}
|
|
return name.(string)
|
|
}
|
|
|
|
// SetIsEnabled sets a flag indicating if account is enabled
|
|
func SetIsEnabled(ctx context.Context, enabled bool) context.Context {
|
|
return context.WithValue(ctx, isEnabledFlag, enabled)
|
|
}
|
|
|
|
// IsEnabled returns the a boolean indicating if the enabled flag is
|
|
// set and is true or false
|
|
func IsEnabled(ctx context.Context) bool {
|
|
elem := ctx.Value(isEnabledFlag)
|
|
if elem == nil {
|
|
return false
|
|
}
|
|
return elem.(bool)
|
|
}
|
|
|
|
// SetAdmin sets the isAdmin flag on the context
|
|
func SetAdmin(ctx context.Context, isAdmin bool) context.Context {
|
|
return context.WithValue(ctx, isAdminKey, isAdmin)
|
|
}
|
|
|
|
// IsAdmin returns a boolean indicating whether
|
|
// or not the context belongs to a logged in user
|
|
// and if that context has the admin flag set
|
|
func IsAdmin(ctx context.Context) bool {
|
|
elem := ctx.Value(isAdminKey)
|
|
if elem == nil {
|
|
return false
|
|
}
|
|
return elem.(bool)
|
|
}
|
|
|
|
// SetUserID sets the userID in the context
|
|
func SetUserID(ctx context.Context, userID string) context.Context {
|
|
return context.WithValue(ctx, UserIDFlag, userID)
|
|
}
|
|
|
|
// UserID returns the userID from the context
|
|
func UserID(ctx context.Context) string {
|
|
userID := ctx.Value(UserIDFlag)
|
|
if userID == nil {
|
|
return ""
|
|
}
|
|
return userID.(string)
|
|
}
|
|
|
|
// GetAdminContext will return an admin context. This can be used internally
|
|
// when fetching users.
|
|
func GetAdminContext(ctx context.Context) context.Context {
|
|
if ctx == nil {
|
|
ctx = context.Background()
|
|
}
|
|
ctx = SetUserID(ctx, "")
|
|
ctx = SetAdmin(ctx, true)
|
|
ctx = SetIsEnabled(ctx, true)
|
|
return ctx
|
|
}
|