This repository has been archived by the owner on Jan 11, 2019. It is now read-only.
-
Notifications
You must be signed in to change notification settings - Fork 79
/
SpecialOAuth2Client.php
232 lines (194 loc) · 7.43 KB
/
SpecialOAuth2Client.php
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
<?php
/**
* SpecialOAuth2Client.php
* Based on TwitterLogin by David Raison, which is based on the guideline published by Dave Challis at http://blogs.ecs.soton.ac.uk/webteam/2010/04/13/254/
* @license: LGPL (GNU Lesser General Public License) http://www.gnu.org/licenses/lgpl.html
*
* @file SpecialOAuth2Client.php
* @ingroup OAuth2Client
*
* @author Joost de Keijzer
*
* Uses the OAuth2 library https://github.com/vznet/oauth_2.0_client_php
*
*/
if ( !defined( 'MEDIAWIKI' ) ) {
die( 'This is a MediaWiki extension, and must be run from within MediaWiki.' );
}
class SpecialOAuth2Client extends SpecialPage {
private $_clientId;
private $_clientSecret;
private $_clientCallbackUrl;
private $_serviceAuthorizeEndpointUrl;
private $_serviceAccessTokenEndpointUrl;
private $_serviceApiEndpointUrl;
private $_euTable = 'external_user';
private $_oAuth2Service;
/**
* Required settings in global $wgOAuth2Client
*
* $wgOAuth2Client['client']['id']
* $wgOAuth2Client['client']['secret']
* //$wgOAuth2Client['client']['callback_url'] // extension should know
*
* $wgOAuth2Client['configuration']['authorize_endpoint']
* $wgOAuth2Client['configuration']['access_token_endpoint']
* $wgOAuth2Client['configuration']['http_bearer_token']
* $wgOAuth2Client['configuration']['query_parameter_token']
* $wgOAuth2Client['configuration']['api_endpoint']
*/
public function __construct() {
if( !self::OAuthEnabled() ) return;
parent::__construct('OAuth2Client'); // ???: wat doet dit?
global $wgOAuth2Client, $wgScriptPath;
global $wgServer, $wgArticlePath;
$client = new OAuth2\Client(
$wgOAuth2Client['client']['id'],
$wgOAuth2Client['client']['secret'],
$wgServer . str_replace( '$1', 'Special:OAuth2Client/callback', $wgArticlePath )
//SpecialPage::getTitleFor( 'OAuth2Client', 'callback' )->getFullURL() // setting variant does not work on specialpages
);
// configuration of service
$configuration = new OAuth2\Service\Configuration(
$wgOAuth2Client['configuration']['authorize_endpoint'],
$wgOAuth2Client['configuration']['access_token_endpoint'],
$wgOAuth2Client['configuration']['http_bearer_token'],
$wgOAuth2Client['configuration']['query_parameter_token']
);
//$configuration->setAuthorizationMethod( OAuth2\Service\Configuration::AUTHORIZATION_METHOD_ALTERNATIVE);
// storage class for access token, just implement OAuth2\DataStore interface for
// your own implementation
$dataStore = new OAuth2\DataStore\Session();
$scope = null;
$this->_oAuth2Service = new OAuth2\Service($client, $configuration, $dataStore, $scope);
}
// default method being called by a specialpage
public function execute( $parameter ){
$this->setHeaders();
switch($parameter){
case 'redirect':
$this->_redirect();
break;
case 'callback':
$this->_handleCallback();
break;
case 'logout':
$this->_logout();
break;
default:
$this->_default();
break;
}
}
private function _redirect() {
if( !self::OAuthEnabled() ) return false;
global $wgRequest;
$_SESSION['returnto'] = $wgRequest->getVal( 'returnto' );
$this->_oAuth2Service->authorize();
}
private function _handleCallback(){
if( !self::OAuthEnabled() ) return false;
global $wgOAuth2Client, $wgOut;
if( $this->_oAuth2Service->getAccessToken() ) {
$requestApiResponse = $this->_oAuth2Service->callApiEndpoint($wgOAuth2Client['configuration']['api_endpoint']);
} else {
throw new MWException('Invalid callback');
}
$user = $this->_userHandling( $requestApiResponse );
$user->setCookies();
if( $user->getRegistration() > wfTimestamp( TS_MW ) - 1 ) {
// new user!
$wgOut->redirect(SpecialPage::getTitleFor('Preferences')->getLinkUrl());
} else {
$title = null;
if( isset( $_SESSION['returnto'] ) ) {
$title = Title::newFromText( $_SESSION['returnto'] );
unset( $_SESSION['returnto'] );
}
if( !$title instanceof Title || 0 > $title->mArticleID ) {
$title = Title::newMainPage();
}
$wgOut->redirect( $title->getFullURL() );
}
return true;
}
private function _logout() {
global $wgOAuth2Client, $wgOut, $wgUser;
if( $wgUser->isLoggedIn() ) $wgUser->logout();
$sevice_name = ( isset( $wgOAuth2Client['configuration']['sevice_name'] ) && 0 < strlen( $wgOAuth2Client['configuration']['sevice_name'] ) ? $wgOAuth2Client['configuration']['sevice_name'] : 'OAuth2' );
$wgOut->setPagetitle( wfMsg( 'oauth2client-logout-header', $sevice_name) );
$wgOut->addWikiMsg( 'oauth2client-logged-out' );
$wgOut->addWikiMsg( 'oauth2client-login-with-oauth2-again', $this->getTitle( 'redirect' )->getPrefixedURL(), $sevice_name );
}
private function _default(){
global $wgOAuth2Client, $wgOut, $wgUser, $wgScriptPath, $wgExtensionAssetsPath;
$sevice_name = ( isset( $wgOAuth2Client['configuration']['sevice_name'] ) && 0 < strlen( $wgOAuth2Client['configuration']['sevice_name'] ) ? $wgOAuth2Client['configuration']['sevice_name'] : 'OAuth2' );
$wgOut->setPagetitle( wfMsg( 'oauth2client-login-header', $sevice_name) );
if ( !$wgUser->isLoggedIn() ) {
$wgOut->addWikiMsg( 'oauth2client-you-can-login-to-this-wiki-with-oauth2', $sevice_name );
$wgOut->addWikiMsg( 'oauth2client-login-with-oauth2', $this->getTitle( 'redirect' )->getPrefixedURL(), $sevice_name );
} else {
$wgOut->addWikiMsg( 'oauth2client-youre-already-loggedin' );
}
return true;
}
protected function _userHandling( $response ) {
global $wgOAuth2Client, $wgAuth;
// TODO: make id, name etc. parameters configurable
$oAuth2Id = $response['id'];
$oAuth2Name = $response['first_name'] .( strlen($response['last_name']) > 0 ? ' ' . $response['last_name'] : '');
// not required
$oAuth2Email = ( isset( $response['email'] ) ? $response['email'] : '' );
$externalId = 'OAuth2Client.' . $wgOAuth2Client['client']['id'] . '.' . $oAuth2Id;
$dbr = wfGetDB( DB_SLAVE );
$row = $dbr->selectRow(
'external_user',
'*',
array( 'eu_external_id' => $externalId )
);
if( $row ) {
// existing OAuth2 user
return User::newFromId( $row->eu_local_id );
}
// create user based on $oAuth2Name
$counter = 1;
$success = false;
while( !$success && $counter <= 1000 ) {
$checkName = $oAuth2Name . ( $counter > 1 ? ' ' . $counter : '' );
$user = User::newFromName( $checkName, 'creatable' );
$counter ++;
$success = (false !== $user && $user->getId() == 0);
}
if( false === $user || $user->getId() != 0 ) {
throw new MWException('Unable to create new user account, please contact the Wiki administrator');
}
$user->setRealName($oAuth2Name);
if( strlen($oAuth2Email) > 0 ) {
$user->setEmail($oAuth2Email);
$user->setEmailAuthenticationTimestamp(time()); // ???: should we auto-authenticate e-mail?
}
if ( $wgAuth->allowPasswordChange() ) {
$user->setPassword(User::randomPassword());
}
$user->addToDatabase();
// link local user to remote OAuth2
$dbw = wfGetDB( DB_MASTER );
$dbw->replace( 'external_user',
array( 'eu_local_id', 'eu_external_id' ),
array( 'eu_local_id' => $user->getId(),
'eu_external_id' => $externalId ),
__METHOD__ );
return $user;
}
static function OAuthEnabled() {
global $wgOAuth2Client;
return isset(
$wgOAuth2Client['client']['id'],
$wgOAuth2Client['client']['secret'],
$wgOAuth2Client['configuration']['authorize_endpoint'],
$wgOAuth2Client['configuration']['access_token_endpoint'],
$wgOAuth2Client['configuration']['http_bearer_token'],
$wgOAuth2Client['configuration']['query_parameter_token']
);
}
}