-
Notifications
You must be signed in to change notification settings - Fork 7
/
session.go
150 lines (120 loc) · 3.46 KB
/
session.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
package sessionup
import (
"context"
"net"
"net/http"
"strings"
"time"
"xojoc.pw/useragent"
)
// Session holds all the data needed to identify a session.
type Session struct {
// Current specifies whether this session's ID
// matches the ID stored in the request's cookie or not.
// NOTE: this field should be omitted by Store interface
// implementations when inserting session into the underlying
// data store.
Current bool `json:"current"`
// CreatedAt specifies a point in time when this session
// was created.
CreatedAt time.Time `json:"created_at"`
// ExpiresAt specifies a point in time when this
// session should become invalid and be deleted
// from the store.
ExpiresAt time.Time `json:"-"`
// ID specifies a unique ID used to find this session
// in the store.
ID string `json:"id"`
// UserKey specifies a non-unique key used to find all
// sessions of the same user.
UserKey string `json:"-"`
// IP specifies an IP address that was used to create
// this session
IP net.IP `json:"ip"`
// Agent specifies the User-Agent data that was used
// to create this session.
Agent struct {
OS string `json:"os"`
Browser string `json:"browser"`
} `json:"agent"`
// Meta specifies a map of metadata associated with
// the session.
Meta map[string]string `json:"meta,omitempty"`
}
// IsValid checks whether the incoming request's properties match
// active session's properties or not.
func (s Session) IsValid(r *http.Request) bool {
ip := true
if len(s.IP) != 0 {
ip = s.IP.Equal(readIP(r))
}
a := useragent.Parse(r.Header.Get("User-Agent"))
os := true
if s.Agent.OS != "" {
os = a != nil && s.Agent.OS == a.OS
}
browser := true
if s.Agent.Browser != "" {
browser = a != nil && s.Agent.Browser == a.Name
}
return ip && os && browser
}
// newSession creates a new Session with the data extracted from
// the provided request, user key and a freshly generated ID.
func (m *Manager) newSession(r *http.Request, key string, meta map[string]string) Session {
s := Session{
CreatedAt: time.Now(),
ExpiresAt: prepExpiresAt(m.expiresIn),
ID: m.genID(),
UserKey: key,
Meta: meta,
}
if m.withIP {
s.IP = readIP(r)
}
if m.withAgent {
a := useragent.Parse(r.Header.Get("User-Agent"))
if a != nil {
s.Agent.OS = a.OS
s.Agent.Browser = a.Name
}
}
return s
}
// prepExpiresAt produces a correct value of expiration time
// used by sessions.
func prepExpiresAt(d time.Duration) time.Time {
if d == 0 {
return time.Time{}
}
return time.Now().Add(d)
}
// readIP tries to extract the real IP of the client from the provided request.
func readIP(r *http.Request) net.IP {
ips := strings.Split(r.Header.Get("X-Forwarded-For"), ", ")
ip := ips[len(ips)-1]
if ip == "" {
ip, _, _ = net.SplitHostPort(r.RemoteAddr)
}
return net.ParseIP(ip)
}
type contextKey int
const sessionKey contextKey = 0
// NewContext creates a new context with the provided Session set as
// a context value.
func NewContext(ctx context.Context, s Session) context.Context {
return context.WithValue(ctx, sessionKey, s)
}
// FromContext extracts Session from the context.
func FromContext(ctx context.Context) (Session, bool) {
s, ok := ctx.Value(sessionKey).(Session)
return s, ok
}
// Meta is a func that handles session's metadata map.
type Meta func(map[string]string)
// MetaEntry adds a new entry into the session's metadata map.
func MetaEntry(key, value string) Meta {
return func(m map[string]string) {
m[key] = value
}
}