1 // Copyright (c) 2012-2016 The Revel Framework Authors, All rights reserved.
2 // Revel Framework source code and usage is governed by a MIT style
3 // license that can be found in the LICENSE file.
11 "github.com/twinj/uuid"
19 // The key for the identity of the session
21 // The expiration date of the session
23 // The value name indicating how long the session should persist - ie should it persist after the browser closes
24 // this is set under the TimestampKey if the session data should expire immediately
25 SessionValueName = "session"
26 // The key container for the json objects of the data, any non strings found in the map will be placed in here
27 // serialized by key using JSON
28 SessionObjectKeyName = "_object_"
29 // The mapped session object
30 SessionMapKeyName = "_map_"
31 // The suffix of the session cookie
32 SessionCookieSuffix = "_SESSION"
35 // Session data, can be any data, there are reserved keywords used by the storage data
36 // SessionIDKey Is the key name for the session
37 // TimestampKey Is the time that the session should expire
39 type Session map[string]interface{}
41 func NewSession() Session {
45 // ID retrieves from the cookie or creates a time-based UUID identifying this
47 func (s Session) ID() string {
48 if sessionIDStr, ok := s[SessionIDKey]; ok {
49 return sessionIDStr.(string)
52 buffer := uuid.NewV4()
54 s[SessionIDKey] = hex.EncodeToString(buffer.Bytes())
55 return s[SessionIDKey].(string)
58 // getExpiration return a time.Time with the session's expiration date.
59 // It uses the passed in expireAfterDuration to add with the current time if the timeout is not
60 // browser dependent (ie session). If previous session has set to "session", the time returned is time.IsZero()
61 func (s Session) GetExpiration(expireAfterDuration time.Duration) time.Time {
62 if expireAfterDuration == 0 || s[TimestampKey] == SessionValueName {
63 // Expire after closing browser
66 return time.Now().Add(expireAfterDuration)
69 // SetNoExpiration sets session to expire when browser session ends
70 func (s Session) SetNoExpiration() {
71 s[TimestampKey] = SessionValueName
74 // SetDefaultExpiration sets session to expire after default duration
75 func (s Session) SetDefaultExpiration() {
76 delete(s, TimestampKey)
79 // sessionTimeoutExpiredOrMissing returns a boolean of whether the session
80 // cookie is either not present or present but beyond its time to live; i.e.,
81 // whether there is not a valid session.
82 func (s Session) SessionTimeoutExpiredOrMissing() bool {
83 if exp, present := s[TimestampKey]; !present {
85 } else if exp == SessionValueName {
87 } else if expInt, _ := strconv.Atoi(exp.(string)); int64(expInt) < time.Now().Unix() {
93 // Constant error if session value is not found
94 var SESSION_VALUE_NOT_FOUND = errors.New("Session value not found")
96 // Get an object or property from the session
97 // it may be embedded inside the session.
98 func (s Session) Get(key string) (newValue interface{}, err error) {
99 // First check to see if it is in the session
100 if v, found := s[key]; found {
103 return s.GetInto(key, nil, false)
106 // Get into the specified value.
107 // If value exists in the session it will just return the value
108 func (s Session) GetInto(key string, target interface{}, force bool) (result interface{}, err error) {
109 if v, found := s[key]; found && !force {
112 splitKey := strings.Split(key, ".")
113 rootKey := splitKey[0]
115 // Force always recreates the object from the session data map
118 if result, err = s.sessionDataFromMap(key); err != nil {
121 } else if result, err = s.sessionDataFromObject(rootKey, target); err != nil {
125 return s.getNestedProperty(splitKey, result)
128 // Attempt to find the key in the session, this is the most generalized form
129 v, found := s[rootKey]
132 // Try to fetch it from the session
134 if v, err = s.sessionDataFromMap(rootKey); err != nil {
137 } else if v, err = s.sessionDataFromObject(rootKey, target); err != nil {
142 return s.getNestedProperty(splitKey, v)
145 // Returns the default value if the key is not found
146 func (s Session) GetDefault(key string, value interface{}, defaultValue interface{}) interface{} {
147 v, e := s.GetInto(key, value, false)
154 // Extract the values from the session
155 func (s Session) GetProperty(key string, value interface{}) (interface{}, error) {
156 // Capitalize the first letter
157 key = strings.Title(key)
159 sessionLog.Info("getProperty", "key", key, "value", value)
161 // For a map it is easy
162 if reflect.TypeOf(value).Kind() == reflect.Map {
163 val := reflect.ValueOf(value)
164 valueOf := val.MapIndex(reflect.ValueOf(key))
165 if valueOf == reflect.Zero(reflect.ValueOf(value).Type()) {
168 //idx := val.MapIndex(reflect.ValueOf(key))
169 if !valueOf.IsValid() {
173 return valueOf.Interface(), nil
176 objValue := s.reflectValue(value)
177 field := objValue.FieldByName(key)
178 if !field.IsValid() {
179 return nil, SESSION_VALUE_NOT_FOUND
182 return field.Interface(), nil
185 // Places the object into the session, a nil value will cause remove the key from the session
186 // (or you can use the Session.Del(key) function
187 func (s Session) Set(key string, value interface{}) error {
197 // Delete the key from the sessionObjects and Session
198 func (s Session) Del(key string) {
199 sessionJsonMap := s.getSessionJsonMap()
200 delete(sessionJsonMap, key)
204 // Extracts the session as a map of [string keys] and json values
205 func (s Session) getSessionJsonMap() map[string]string {
206 if sessionJson, found := s[SessionObjectKeyName]; found {
207 if _, valid := sessionJson.(map[string]string); !valid {
208 sessionLog.Error("Session object key corrupted, reset", "was", sessionJson)
209 s[SessionObjectKeyName] = map[string]string{}
211 // serialized data inside the session _objects
213 s[SessionObjectKeyName] = map[string]string{}
216 return s[SessionObjectKeyName].(map[string]string)
219 // Convert the map to a simple map[string]string map
220 // this will marshal any non string objects encountered and store them the the jsonMap
221 // The expiration time will also be assigned
222 func (s Session) Serialize() map[string]string {
223 sessionJsonMap := s.getSessionJsonMap()
224 newMap := map[string]string{}
225 newObjectMap := map[string]string{}
226 for key, value := range sessionJsonMap {
227 newObjectMap[key] = value
229 for key, value := range s {
230 if key == SessionObjectKeyName || key == SessionMapKeyName {
233 if reflect.ValueOf(value).Kind() == reflect.String {
234 newMap[key] = value.(string)
237 println("Serialize the data for", key)
238 if data, err := json.Marshal(value); err != nil {
239 sessionLog.Error("Unable to marshal session ", "key", key, "error", err)
242 newObjectMap[key] = string(data)
245 if len(newObjectMap) > 0 {
246 if data, err := json.Marshal(newObjectMap); err != nil {
247 sessionLog.Error("Unable to marshal session ", "key", SessionObjectKeyName, "error", err)
250 newMap[SessionObjectKeyName] = string(data)
257 // Set the session object from the loaded data
258 func (s Session) Load(data map[string]string) {
259 for key, value := range data {
260 if key == SessionObjectKeyName {
261 target := map[string]string{}
262 if err := json.Unmarshal([]byte(value), &target); err != nil {
263 sessionLog.Error("Unable to unmarshal session ", "key", SessionObjectKeyName, "error", err)
274 // Checks to see if the session is empty
275 func (s Session) Empty() bool {
279 if k == SessionObjectKeyName || k == SessionMapKeyName {
286 func (s *Session) reflectValue(obj interface{}) reflect.Value {
287 var val reflect.Value
289 if reflect.TypeOf(obj).Kind() == reflect.Ptr {
290 val = reflect.ValueOf(obj).Elem()
292 val = reflect.ValueOf(obj)
298 // Starting at position 1 drill into the object
299 func (s Session) getNestedProperty(keys []string, newValue interface{}) (result interface{}, err error) {
300 for x := 1; x < len(keys); x++ {
301 newValue, err = s.GetProperty(keys[x], newValue)
302 if err != nil || newValue == nil {
309 // Always converts the data from the session mapped objects into the target,
310 // it will store the results under the session key name SessionMapKeyName
311 func (s Session) sessionDataFromMap(key string) (result interface{}, err error) {
312 var mapValue map[string]interface{}
313 uncastMapValue, found := s[SessionMapKeyName]
315 mapValue = map[string]interface{}{}
316 s[SessionMapKeyName] = mapValue
317 } else if mapValue, found = uncastMapValue.(map[string]interface{}); !found {
318 // Unusual means that the value in the session was not expected
319 sessionLog.Errorf("Unusual means that the value in the session was not expected", "session", uncastMapValue)
320 mapValue = map[string]interface{}{}
321 s[SessionMapKeyName] = mapValue
324 // Try to extract the key from the map
325 result, found = mapValue[key]
327 result, err = s.convertSessionData(key, nil)
329 mapValue[key] = result
335 // Unpack the object from the session map and store it in the session when done, if no error occurs
336 func (s Session) sessionDataFromObject(key string, newValue interface{}) (result interface{}, err error) {
337 result, err = s.convertSessionData(key, newValue)
345 // Converts from the session json map into the target,
346 func (s Session) convertSessionData(key string, target interface{}) (result interface{}, err error) {
347 sessionJsonMap := s.getSessionJsonMap()
348 v, found := sessionJsonMap[key]
350 return target, SESSION_VALUE_NOT_FOUND
353 // Create a target if needed
355 target = map[string]interface{}{}
356 if err := json.Unmarshal([]byte(v), &target); err != nil {
359 } else if err := json.Unmarshal([]byte(v), target); err != nil {