X-Git-Url: https://gerrit.akraino.org/r/gitweb?a=blobdiff_plain;f=src%2Ffoundation%2Fapi%2Frevel%2Fsession%2Fsession.go;fp=src%2Ffoundation%2Fapi%2Frevel%2Fsession%2Fsession.go;h=c5696bc820037f29dd8ac16a5f113685eb82e82a;hb=1d1ee6961c93781e1187d8c7faa868da6b2f01f4;hp=0000000000000000000000000000000000000000;hpb=56dd5e0f2164b37b40ac1daa188ccc618b4cbd19;p=iec.git diff --git a/src/foundation/api/revel/session/session.go b/src/foundation/api/revel/session/session.go new file mode 100644 index 0000000..c5696bc --- /dev/null +++ b/src/foundation/api/revel/session/session.go @@ -0,0 +1,364 @@ +// Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved. +// Revel Framework source code and usage is governed by a MIT style +// license that can be found in the LICENSE file. + +package session + +import ( + "encoding/hex" + "encoding/json" + "errors" + "github.com/twinj/uuid" + "reflect" + "strconv" + "strings" + "time" +) + +const ( + // The key for the identity of the session + SessionIDKey = "_ID" + // The expiration date of the session + TimestampKey = "_TS" + // The value name indicating how long the session should persist - ie should it persist after the browser closes + // this is set under the TimestampKey if the session data should expire immediately + SessionValueName = "session" + // The key container for the json objects of the data, any non strings found in the map will be placed in here + // serialized by key using JSON + SessionObjectKeyName = "_object_" + // The mapped session object + SessionMapKeyName = "_map_" + // The suffix of the session cookie + SessionCookieSuffix = "_SESSION" +) + +// Session data, can be any data, there are reserved keywords used by the storage data +// SessionIDKey Is the key name for the session +// TimestampKey Is the time that the session should expire +// +type Session map[string]interface{} + +func NewSession() Session { + return Session{} +} + +// ID retrieves from the cookie or creates a time-based UUID identifying this +// session. +func (s Session) ID() string { + if sessionIDStr, ok := s[SessionIDKey]; ok { + return sessionIDStr.(string) + } + + buffer := uuid.NewV4() + + s[SessionIDKey] = hex.EncodeToString(buffer.Bytes()) + return s[SessionIDKey].(string) +} + +// getExpiration return a time.Time with the session's expiration date. +// It uses the passed in expireAfterDuration to add with the current time if the timeout is not +// browser dependent (ie session). If previous session has set to "session", the time returned is time.IsZero() +func (s Session) GetExpiration(expireAfterDuration time.Duration) time.Time { + if expireAfterDuration == 0 || s[TimestampKey] == SessionValueName { + // Expire after closing browser + return time.Time{} + } + return time.Now().Add(expireAfterDuration) +} + +// SetNoExpiration sets session to expire when browser session ends +func (s Session) SetNoExpiration() { + s[TimestampKey] = SessionValueName +} + +// SetDefaultExpiration sets session to expire after default duration +func (s Session) SetDefaultExpiration() { + delete(s, TimestampKey) +} + +// sessionTimeoutExpiredOrMissing returns a boolean of whether the session +// cookie is either not present or present but beyond its time to live; i.e., +// whether there is not a valid session. +func (s Session) SessionTimeoutExpiredOrMissing() bool { + if exp, present := s[TimestampKey]; !present { + return true + } else if exp == SessionValueName { + return false + } else if expInt, _ := strconv.Atoi(exp.(string)); int64(expInt) < time.Now().Unix() { + return true + } + return false +} + +// Constant error if session value is not found +var SESSION_VALUE_NOT_FOUND = errors.New("Session value not found") + +// Get an object or property from the session +// it may be embedded inside the session. +func (s Session) Get(key string) (newValue interface{}, err error) { + // First check to see if it is in the session + if v, found := s[key]; found { + return v, nil + } + return s.GetInto(key, nil, false) +} + +// Get into the specified value. +// If value exists in the session it will just return the value +func (s Session) GetInto(key string, target interface{}, force bool) (result interface{}, err error) { + if v, found := s[key]; found && !force { + return v, nil + } + splitKey := strings.Split(key, ".") + rootKey := splitKey[0] + + // Force always recreates the object from the session data map + if force { + if target == nil { + if result, err = s.sessionDataFromMap(key); err != nil { + return + } + } else if result, err = s.sessionDataFromObject(rootKey, target); err != nil { + return + } + + return s.getNestedProperty(splitKey, result) + } + + // Attempt to find the key in the session, this is the most generalized form + v, found := s[rootKey] + if !found { + if target == nil { + // Try to fetch it from the session + + if v, err = s.sessionDataFromMap(rootKey); err != nil { + return + } + } else if v, err = s.sessionDataFromObject(rootKey, target); err != nil { + return + } + } + + return s.getNestedProperty(splitKey, v) +} + +// Returns the default value if the key is not found +func (s Session) GetDefault(key string, value interface{}, defaultValue interface{}) interface{} { + v, e := s.GetInto(key, value, false) + if e != nil { + v = defaultValue + } + return v +} + +// Extract the values from the session +func (s Session) GetProperty(key string, value interface{}) (interface{}, error) { + // Capitalize the first letter + key = strings.Title(key) + + sessionLog.Info("getProperty", "key", key, "value", value) + + // For a map it is easy + if reflect.TypeOf(value).Kind() == reflect.Map { + val := reflect.ValueOf(value) + valueOf := val.MapIndex(reflect.ValueOf(key)) + if valueOf == reflect.Zero(reflect.ValueOf(value).Type()) { + return nil, nil + } + //idx := val.MapIndex(reflect.ValueOf(key)) + if !valueOf.IsValid() { + return nil, nil + } + + return valueOf.Interface(), nil + } + + objValue := s.reflectValue(value) + field := objValue.FieldByName(key) + if !field.IsValid() { + return nil, SESSION_VALUE_NOT_FOUND + } + + return field.Interface(), nil +} + +// Places the object into the session, a nil value will cause remove the key from the session +// (or you can use the Session.Del(key) function +func (s Session) Set(key string, value interface{}) error { + if value == nil { + s.Del(key) + return nil + } + + s[key] = value + return nil +} + +// Delete the key from the sessionObjects and Session +func (s Session) Del(key string) { + sessionJsonMap := s.getSessionJsonMap() + delete(sessionJsonMap, key) + delete(s, key) +} + +// Extracts the session as a map of [string keys] and json values +func (s Session) getSessionJsonMap() map[string]string { + if sessionJson, found := s[SessionObjectKeyName]; found { + if _, valid := sessionJson.(map[string]string); !valid { + sessionLog.Error("Session object key corrupted, reset", "was", sessionJson) + s[SessionObjectKeyName] = map[string]string{} + } + // serialized data inside the session _objects + } else { + s[SessionObjectKeyName] = map[string]string{} + } + + return s[SessionObjectKeyName].(map[string]string) +} + +// Convert the map to a simple map[string]string map +// this will marshal any non string objects encountered and store them the the jsonMap +// The expiration time will also be assigned +func (s Session) Serialize() map[string]string { + sessionJsonMap := s.getSessionJsonMap() + newMap := map[string]string{} + newObjectMap := map[string]string{} + for key, value := range sessionJsonMap { + newObjectMap[key] = value + } + for key, value := range s { + if key == SessionObjectKeyName || key == SessionMapKeyName { + continue + } + if reflect.ValueOf(value).Kind() == reflect.String { + newMap[key] = value.(string) + continue + } + println("Serialize the data for", key) + if data, err := json.Marshal(value); err != nil { + sessionLog.Error("Unable to marshal session ", "key", key, "error", err) + continue + } else { + newObjectMap[key] = string(data) + } + } + if len(newObjectMap) > 0 { + if data, err := json.Marshal(newObjectMap); err != nil { + sessionLog.Error("Unable to marshal session ", "key", SessionObjectKeyName, "error", err) + + } else { + newMap[SessionObjectKeyName] = string(data) + } + } + + return newMap +} + +// Set the session object from the loaded data +func (s Session) Load(data map[string]string) { + for key, value := range data { + if key == SessionObjectKeyName { + target := map[string]string{} + if err := json.Unmarshal([]byte(value), &target); err != nil { + sessionLog.Error("Unable to unmarshal session ", "key", SessionObjectKeyName, "error", err) + } else { + s[key] = target + } + } else { + s[key] = value + } + + } +} + +// Checks to see if the session is empty +func (s Session) Empty() bool { + i := 0 + for k := range s { + i++ + if k == SessionObjectKeyName || k == SessionMapKeyName { + continue + } + } + return i == 0 +} + +func (s *Session) reflectValue(obj interface{}) reflect.Value { + var val reflect.Value + + if reflect.TypeOf(obj).Kind() == reflect.Ptr { + val = reflect.ValueOf(obj).Elem() + } else { + val = reflect.ValueOf(obj) + } + + return val +} + +// Starting at position 1 drill into the object +func (s Session) getNestedProperty(keys []string, newValue interface{}) (result interface{}, err error) { + for x := 1; x < len(keys); x++ { + newValue, err = s.GetProperty(keys[x], newValue) + if err != nil || newValue == nil { + return newValue, err + } + } + return newValue, nil +} + +// Always converts the data from the session mapped objects into the target, +// it will store the results under the session key name SessionMapKeyName +func (s Session) sessionDataFromMap(key string) (result interface{}, err error) { + var mapValue map[string]interface{} + uncastMapValue, found := s[SessionMapKeyName] + if !found { + mapValue = map[string]interface{}{} + s[SessionMapKeyName] = mapValue + } else if mapValue, found = uncastMapValue.(map[string]interface{}); !found { + // Unusual means that the value in the session was not expected + sessionLog.Errorf("Unusual means that the value in the session was not expected", "session", uncastMapValue) + mapValue = map[string]interface{}{} + s[SessionMapKeyName] = mapValue + } + + // Try to extract the key from the map + result, found = mapValue[key] + if !found { + result, err = s.convertSessionData(key, nil) + if err == nil { + mapValue[key] = result + } + } + return +} + +// Unpack the object from the session map and store it in the session when done, if no error occurs +func (s Session) sessionDataFromObject(key string, newValue interface{}) (result interface{}, err error) { + result, err = s.convertSessionData(key, newValue) + if err != nil { + return + } + s[key] = result + return +} + +// Converts from the session json map into the target, +func (s Session) convertSessionData(key string, target interface{}) (result interface{}, err error) { + sessionJsonMap := s.getSessionJsonMap() + v, found := sessionJsonMap[key] + if !found { + return target, SESSION_VALUE_NOT_FOUND + } + + // Create a target if needed + if target == nil { + target = map[string]interface{}{} + if err := json.Unmarshal([]byte(v), &target); err != nil { + return target, err + } + } else if err := json.Unmarshal([]byte(v), target); err != nil { + return target, err + } + result = target + return +}