csna.module
9.12 KB
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
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
<?php
/**
* @file
* The Chinese Social Networks Authentication (CSNA) module provides a global
* framework and API to allow other modules to define specific authentication
* methods from Chinese Social Networks through a supported OAuth protocol.
*/
define('CSNA_OAUTH2', 'oauth2');
/**
* Implements hook_menu().
*/
function csna_menu() {
$items['csna/%csna_provider'] = array(
'title' => 'CSNA Redirect',
'page callback' => 'csna_redirect',
'page arguments' => array(1),
'access callback' => 'user_is_anonymous',
'type' => MENU_CALLBACK,
);
$items['csna/%csna_provider/callback'] = array(
'title' => 'Authentication callback',
'page callback' => 'csna_callback',
'page arguments' => array(1),
'access callback' => 'user_is_anonymous',
'type' => MENU_CALLBACK,
);
$items['csna/%user/complete-register'] = array(
'title' => t('Complete register'),
'page callback' => 'drupal_get_form',
'page arguments' => array('csna_complete_register_form', 1),
'access callback' => 'csna_user_edit_access',
'access arguments' => array(1),
'file' => 'csna.pages.inc',
);
$items['admin/config/services/csna'] = array(
'title' => 'Chinese Social Networks Authentication settings (CSNA)',
'description' => 'Configure the CSNA key or secrect code settings for third party authentication API providers.',
'page callback' => 'drupal_get_form',
'page arguments' => array('csna_admin_settings'),
'access arguments' => array('administer csna'),
'file' => 'csna.admin.inc',
);
return $items;
}
/**
* Check the Provider ID.
*
* @param string $provider_id
* The Provider ID.
*
* @return mixed
* A value of $providers[$provider_id] from csna_providers(),
* or FALSE if the value does not exist.
*/
function csna_provider_load($provider_id) {
$providers = csna_providers();
return isset($providers[$provider_id]) ? $providers[$provider_id] : FALSE;
}
/**
* Implements hook_perm().
*/
function csna_permission() {
return array(
'administer csna' => array(
'title' => t('Administer CSNA'),
'description' => t('Perform administration tasks for CSNA.'),
),
);
}
/**
* Implements hook_theme().
*/
function csna_theme($existing, $type, $theme, $path) {
$providers = csna_providers();
return array(
'csna_providers' => array(
'variables' => array('providers' => $providers),
),
);
}
/**
* Menu callback: CSNA Redirect.
*/
function csna_redirect($provider) {
$parameters = array(
'client_id' => $provider['key'],
'redirect_uri' => $provider['callback'],
'response_type' => 'code',
);
if ($provider['provider_id'] == 'wechat') {
unset($parameters['client_id']);
$parameters['appid'] = $provider['key'];
$parameters['scope'] = 'snsapi_login';
}
drupal_goto($provider['authorize_uri'], array('query' => $parameters));
}
/**
* Menu callback: CSNA Callback.
*/
function csna_callback($provider) {
global $user;
$data = module_invoke($provider['module'], 'csna_provider_callback', $provider, $_REQUEST);
// Forcing array format because of Sina weibo will return an Object.
if (!is_array($data)) {
$data = (array) $data;
}
if (isset($data['access_token'])) {
$identity = $provider['provider_id'] . '_' . $data['uid'];
$account = user_external_load($identity);
if (!isset($account->uid)) {
$name = $data['name'];
$ac = user_load_by_name($name);
if ($ac) {
$separator = '_';
$i = 0;
do {
$i++;
$name = $data['name'] . $separator . $i;
} while (user_load_by_name($name));
}
//User avatar
if (isset($data['avatar_link'])) {
$avatar_directory = file_default_scheme() . '://' . variable_get('user_picture_path', 'pictures');
$avatar = system_retrieve_file($data['avatar_link'], $avatar_directory, TRUE, FILE_EXISTS_RENAME);
}
$edit = array(
'name' => $name,
'status' => 1,
'picture' => isset($avatar->fid) ? $avatar->fid : '',
);
$account = user_save(NULL, $edit);
user_set_authmaps($account, array('authname_csna' => $identity));
}
csna_execute_user_login($account);
if (variable_get('csna_require_email', TRUE) && empty($account->mail)) {
drupal_goto('csna/' . $account->uid . '/complete-register');
}
}
drupal_goto('user');
}
/**
* Helper function for CSNA Callback.
*
* It executes user login after returning from provider's authentication page.
* If no valid account is found or authentication with providers failed, this
* function returns FALSE.
*/
function csna_execute_user_login($account) {
if (empty($account) OR !isset($account->uid)) {
return FALSE;
}
$form_state['uid'] = $account->uid;
user_login_submit(array(), $form_state);
return TRUE;
}
/**
* Implements hook_form_alter().
*/
function csna_form_alter(&$form, &$form_state, $form_id) {
if ($form_id == 'user_login' || $form_id == 'user_login_block' || $form_id == 'user_register_form') {
$providers = csna_providers();
// if admin form
if (empty($form['administer_users']['#value'])) {
if (isset($form['submitted']['user_login_block']['#suffix']) && !empty($form['submitted']['user_login_block']['#suffix'])) {
$form['submitted']['user_login_block']['#suffix'] .= theme('csna_providers', array('providers' => $providers));
}
else {
$form['submitted']['user_login_block']['#suffix'] = theme('csna_providers', array('providers' => $providers));
}
}
}
}
/**
* Returns an array of OAuth Third Party authentication providers.
*
* CSNA's API Framework provides a method for any enabled module to define
* additional authentication providers. This function gathers information on
* provider authentication methods defined by enabled modules.
*
* @return array
* An associative array of providers keyed by the provider_id.
*
* @see hook_csna_provider_info()
*/
function csna_providers($valid = FALSE) {
static $providers;
if (!isset($providers)) {
$providers = array();
// Build the providers array, including module names for the purpose
// of including files if necessary.
// An associative array of providers objects keyed by the provider_id.
foreach (module_implements('csna_provider_info') as $module) {
foreach (module_invoke($module, 'csna_provider_info') as $provider_id => $provider) {
$provider['provider_id'] = $provider_id;
$provider['module'] = $module;
$provider['key'] = variable_get('csna_' . $provider_id . '_key', '');
$provider['secret'] = variable_get('csna_' . $provider_id . '_secret', '');
$providers[$provider_id] = $provider;
if ($valid && (empty($provider['key']) || empty($provider['secret']))) {
unset($providers[$provider_id]);
}
}
}
drupal_alter('csna_provider_info', $providers);
foreach ($providers as $provider_id => & $provider) {
$defaults = array(
'provider_id' => $provider_id,
'base' => $provider_id,
'title' => '',
'description' => '',
'active' => FALSE,
'callback' => url('csna/' . $provider_id . '/callback', array('absolute' => TRUE)),
'file' => '',
);
$provider += $defaults;
// Default the display title to the title if necessary. The display
// title is used in instances where the login method has an official
// name but a different method of display on selection forms (like some
// text and a set of images).
if (empty($provider['display_title'])) {
$provider['display_title'] = $provider['title'];
}
// Default the short title to the title if necessary. Like the display
// title, the short title is an alternate way of displaying the title to
// the user consisting of plain text but with unnecessary information
// stripped off.
if (empty($provider[$provider_id]['short_title'])) {
$provider['short_title'] = $provider['title'];
}
}
}
return $providers;
}
/**
* Render a themed list of links to providers authorized login URL pages.
*
* @param array $providers
* An associative array of providers keyed by the provider_id, as returned by
* the function csna_providers().
* Mostly the display_title property of providers is used in this function
* to display a custom title, name or even HTML to be linked to corresponding
* provider's authorized login URL page. See API documentation for more
* information on providers available properties.
*
* @see csna_providers()
* @see hook_csna_provider_info()
*/
function theme_csna_providers($vars) {
$providers = $vars['providers'];
$items = array();
foreach ($providers as $provider_id => $provider) {
$items[] = array(
'data' => l($provider['display_title'], 'csna/' . $provider_id, array('html' => TRUE)),
'id' => 'csna_provider_' . $provider_id,
);
}
return theme('item_list', array('items' => $items, 'attributes' => array('class' => 'csna-items')));
}
/**
* Access callback for CSNA complete register.
*
* @param $account
*/
function csna_user_edit_access($account) {
return $GLOBALS['user']->uid == $account->uid;
}