Skip to content

Commit

Permalink
fix(cxl-host): generate unique cxl host session id (#53)
Browse files Browse the repository at this point in the history
* fix(cxl-host): change session id string to be a random, 10 char alpha-numeric string

* fix(cxl-host): fix pointer assignment and update error handling

* refactor(cxl-host): cleanup unused varaibles
  • Loading branch information
scott-howe-1 authored Nov 19, 2024
1 parent f830661 commit 076d4b7
Show file tree
Hide file tree
Showing 2 changed files with 74 additions and 28 deletions.
10 changes: 7 additions & 3 deletions cmd/cxl-host/service/cxl_host_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -694,9 +694,13 @@ func (s *CxlHostApiService) RedfishV1SessionServiceSessionsPost(ctx context.Cont
}

// Create the session
session := accounts.CreateSession(ctx, *sessionV171Session.UserName, *sessionV171Session.Password)
if session == nil {
return redfishapi.Response(http.StatusUnauthorized, nil), errors.New("Invalid user credentials")
session, err := accounts.CreateSession(ctx, *sessionV171Session.UserName, *sessionV171Session.Password)
if err != nil {
if errors.Is(err, accounts.ErrInvalidCredentials) {
return redfishapi.Response(http.StatusUnauthorized, nil), err
} else if errors.Is(err, accounts.ErrGenerateSessionId) {
return redfishapi.Response(http.StatusInternalServerError, nil), err
}
}

resource := FillInSessionResource(session.Id, session, true)
Expand Down
92 changes: 67 additions & 25 deletions pkg/accounts/sessions.go
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ package accounts

import (
"context"
"errors"
"fmt"
"math"
"strconv"
"math/rand"
"strings"
"time"

Expand All @@ -16,12 +16,8 @@ import (

const (
defaultSessionSeconds float64 = 60 * 30.0
maxSessionId int = math.MaxInt
)

// Track the last session id used, increment until a max is reached, then reset to 1
var activeSessionId int = 0

type SessionInformation struct {
Id string
Token string
Expand All @@ -38,42 +34,43 @@ func init() {
sessions = make(map[string]*SessionInformation)
}

var (
ErrInvalidCredentials = errors.New("invalid user credentials")
ErrGenerateSessionId = errors.New("session id creation failure")
)

// CreateSession: create a new session and return it's information
func CreateSession(ctx context.Context, username, password string) *SessionInformation {
func CreateSession(ctx context.Context, username, password string) (*SessionInformation, error) {
logger := klog.FromContext(ctx)

var session *SessionInformation

// Validate user credentials
valid, err := AccountsHandler().ValidAccount(username, password)
if valid && err == nil {
session = CreateSessionToken(ctx, username)
if !valid || err != nil {
logger.V(1).Error(err, "failure: create session", "username", username)
return nil, ErrInvalidCredentials
}

return session
}

// CreateSessionToken: create a token and add it to the map, return a new session id
func CreateSessionToken(ctx context.Context, user string) *SessionInformation {
logger := klog.FromContext(ctx)

// Example uuid: ee0328d9-258a-4e81-976e-b75aa4a2d8f5
token := uuid.New().String()
token = strings.ReplaceAll(token, "-", "")

// Determine the next session id to use
activeSessionId += 1
if activeSessionId > maxSessionId {
logger.V(1).Info("session id was reset", "maxSessionId", maxSessionId)
activeSessionId = 1
// Generate a new session id
id, err := GenerateSessionId()
if err != nil {
logger.V(1).Error(err, "failure: generate session id", "username", username)
return nil, ErrGenerateSessionId
}
id := strconv.Itoa(activeSessionId)

// Store the session information using the session id (an integer from 1..max)
// Store the session information using the session id
created := time.Now()
session := &SessionInformation{Id: id, Token: token, Created: created, Updated: created, Username: user, Timeout: defaultSessionSeconds}
session = &SessionInformation{Id: id, Token: token, Created: created, Updated: created, Username: username, Timeout: defaultSessionSeconds}
sessions[id] = session

return session
logger.V(1).Info("success: created session:", "session", session)

return session, nil
}

// GetSessions: retrieve the map of all sessions
Expand Down Expand Up @@ -165,3 +162,48 @@ func DeleteSession(ctx context.Context, id string) *SessionInformation {
return info
}
}

const randomCharset = "abcdefghijklmnopqrstuvwxyz" +
"ABCDEFGHIJKLMNOPQRSTUVWXYZ" +
"0123456789"

func randomString(r *rand.Rand, length int) string {
b := make([]byte, length)
for i := range b {
b[i] = randomCharset[r.Intn(len(randomCharset))]
}
return string(b)
}

// generateUniqueKey - Generic function for generating a unique key for an existing map
func generateUniqueKey(r *rand.Rand, length int, existingMap map[string]interface{}, maxAttempts int) (string, error) {
for attempts := 0; attempts < maxAttempts; attempts++ {
key := randomString(r, length)
if _, exists := existingMap[key]; !exists {
return key, nil
}
}
return "", fmt.Errorf("failed to generate a unique key after maximum attempts")
}

// GenerateSessionId - Generates a new session id, consisting of 10 random alpha-numeric chars
func GenerateSessionId() (string, error) {
seed := rand.NewSource(time.Now().UnixNano())
r := rand.New(seed)
maxAttempts := 100
keyLength := 10

// Convert sessions map to map[string]interface{}
interfaceMap := make(map[string]interface{})
for k, v := range sessions {
interfaceMap[k] = v
}

// Example of generating and storing unique keys
id, err := generateUniqueKey(r, keyLength, interfaceMap, maxAttempts)
if err != nil {
return "", fmt.Errorf("failure: session id generation: %s", err)
}

return id, nil
}

0 comments on commit 076d4b7

Please sign in to comment.