@@ -14,10 +14,10 @@ public function loadUserForSession($session_type, $session_key) {
14
14
$ conn_r ,
15
15
'SELECT s.sessionExpires AS _sessionExpires, s.id AS _sessionID, u.*
16
16
FROM %T u JOIN %T s ON u.phid = s.userPHID
17
- AND s.type LIKE %> AND s.sessionKey = %s ' ,
17
+ AND s.type = %s AND s.sessionKey = %s ' ,
18
18
$ user_table ->getTableName (),
19
19
$ session_table ->getTableName (),
20
- $ session_type. ' - ' ,
20
+ $ session_type ,
21
21
PhabricatorHash::digest ($ session_key ));
22
22
23
23
if (!$ info ) {
@@ -72,142 +72,35 @@ public function establishSession($session_type, $identity_phid) {
72
72
$ session_table = new PhabricatorAuthSession ();
73
73
$ conn_w = $ session_table ->establishConnection ('w ' );
74
74
75
- if (strpos ($ session_type , '- ' ) !== false ) {
76
- throw new Exception ("Session type must not contain hyphen ('-')! " );
77
- }
78
-
79
- // We allow multiple sessions of the same type, so when a caller requests
80
- // a new session of type "web", we give them the first available session in
81
- // "web-1", "web-2", ..., "web-N", up to some configurable limit. If none
82
- // of these sessions is available, we overwrite the oldest session and
83
- // reissue a new one in its place.
84
-
85
- $ session_limit = 1 ;
86
- switch ($ session_type ) {
87
- case PhabricatorAuthSession::TYPE_WEB :
88
- $ session_limit = PhabricatorEnv::getEnvConfig ('auth.sessions.web ' );
89
- break ;
90
- case PhabricatorAuthSession::TYPE_CONDUIT :
91
- $ session_limit = PhabricatorEnv::getEnvConfig ('auth.sessions.conduit ' );
92
- break ;
93
- default :
94
- throw new Exception ("Unknown session type ' {$ session_type }'! " );
95
- }
96
-
97
- $ session_limit = (int )$ session_limit ;
98
- if ($ session_limit <= 0 ) {
99
- throw new Exception (
100
- "Session limit for ' {$ session_type }' must be at least 1! " );
101
- }
102
-
103
- // NOTE: Session establishment is sensitive to race conditions, as when
104
- // piping `arc` to `arc`:
105
- //
106
- // arc export ... | arc paste ...
107
- //
108
- // To avoid this, we overwrite an old session only if it hasn't been
109
- // re-established since we read it.
110
-
111
75
// Consume entropy to generate a new session key, forestalling the eventual
112
76
// heat death of the universe.
113
77
$ session_key = Filesystem::readRandomCharacters (40 );
114
- $ session_ttl = PhabricatorAuthSession::getSessionTypeTTL ($ session_type );
115
-
116
- // Load all the currently active sessions.
117
- $ sessions = queryfx_all (
118
- $ conn_w ,
119
- 'SELECT type, sessionKey, sessionStart FROM %T
120
- WHERE userPHID = %s AND type LIKE %> ' ,
121
- $ session_table ->getTableName (),
122
- $ identity_phid ,
123
- $ session_type .'- ' );
124
- $ sessions = ipull ($ sessions , null , 'type ' );
125
- $ sessions = isort ($ sessions , 'sessionStart ' );
126
78
127
- $ existing_sessions = array_keys ($ sessions );
79
+ // This has a side effect of validating the session type.
80
+ $ session_ttl = PhabricatorAuthSession::getSessionTypeTTL ($ session_type );
128
81
129
- // UNGUARDED WRITES: Logging-in users don't have CSRF stuff yet.
82
+ // Logging-in users don't have CSRF stuff yet, so we have to unguard this
83
+ // write.
130
84
$ unguarded = AphrontWriteGuard::beginScopedUnguardedWrites ();
131
-
132
- $ retries = 0 ;
133
- while (true ) {
134
-
135
- // Choose which 'type' we'll actually establish, i.e. what number we're
136
- // going to append to the basic session type. To do this, just check all
137
- // the numbers sequentially until we find an available session.
138
- $ establish_type = null ;
139
- for ($ ii = 1 ; $ ii <= $ session_limit ; $ ii ++) {
140
- $ try_type = $ session_type .'- ' .$ ii ;
141
- if (!in_array ($ try_type , $ existing_sessions )) {
142
- $ establish_type = $ try_type ;
143
- $ expect_key = PhabricatorHash::digest ($ session_key );
144
- $ existing_sessions [] = $ try_type ;
145
-
146
- // Ensure the row exists so we can issue an update below. We don't
147
- // care if we race here or not.
148
- queryfx (
149
- $ conn_w ,
150
- 'INSERT IGNORE INTO %T
151
- (userPHID, type, sessionKey, sessionStart, sessionExpires)
152
- VALUES (%s, %s, %s, 0, UNIX_TIMESTAMP() + %d) ' ,
153
- $ session_table ->getTableName (),
154
- $ identity_phid ,
155
- $ establish_type ,
156
- PhabricatorHash::digest ($ session_key ),
157
- $ session_ttl );
158
- break ;
159
- }
160
- }
161
-
162
- // If we didn't find an available session, choose the oldest session and
163
- // overwrite it.
164
- if (!$ establish_type ) {
165
- $ oldest = reset ($ sessions );
166
- $ establish_type = $ oldest ['type ' ];
167
- $ expect_key = $ oldest ['sessionKey ' ];
168
- }
169
-
170
- // This is so that we'll only overwrite the session if it hasn't been
171
- // refreshed since we read it. If it has, the session key will be
172
- // different and we know we're racing other processes. Whichever one
173
- // won gets the session, we go back and try again.
174
-
175
- queryfx (
176
- $ conn_w ,
177
- 'UPDATE %T SET sessionKey = %s, sessionStart = UNIX_TIMESTAMP(),
178
- sessionExpires = UNIX_TIMESTAMP() + %d
179
- WHERE userPHID = %s AND type = %s AND sessionKey = %s ' ,
180
- $ session_table ->getTableName (),
181
- PhabricatorHash::digest ($ session_key ),
182
- $ session_ttl ,
85
+ id (new PhabricatorAuthSession ())
86
+ ->setUserPHID ($ identity_phid )
87
+ ->setType ($ session_type )
88
+ ->setSessionKey (PhabricatorHash::digest ($ session_key ))
89
+ ->setSessionStart (time ())
90
+ ->setSessionExpires (time () + $ session_ttl )
91
+ ->save ();
92
+
93
+ $ log = PhabricatorUserLog::initializeNewLog (
94
+ null ,
183
95
$ identity_phid ,
184
- $ establish_type ,
185
- $ expect_key );
186
-
187
- if ($ conn_w ->getAffectedRows ()) {
188
- // The update worked, so the session is valid.
189
- break ;
190
- } else {
191
- // We know this just got grabbed, so don't try it again.
192
- unset($ sessions [$ establish_type ]);
193
- }
194
-
195
- if (++$ retries > $ session_limit ) {
196
- throw new Exception ("Failed to establish a session! " );
197
- }
198
- }
199
-
200
- $ log = PhabricatorUserLog::initializeNewLog (
201
- null ,
202
- $ identity_phid ,
203
- PhabricatorUserLog::ACTION_LOGIN );
204
- $ log ->setDetails (
205
- array (
206
- 'session_type ' => $ session_type ,
207
- 'session_issued ' => $ establish_type ,
208
- ));
209
- $ log ->setSession ($ session_key );
210
- $ log ->save ();
96
+ PhabricatorUserLog::ACTION_LOGIN );
97
+ $ log ->setDetails (
98
+ array (
99
+ 'session_type ' => $ session_type ,
100
+ ));
101
+ $ log ->setSession ($ session_key );
102
+ $ log ->save ();
103
+ unset($ unguarded );
211
104
212
105
return $ session_key ;
213
106
}
0 commit comments