Code Coverage |
||||||||||
Classes and Traits |
Functions and Methods |
Lines |
||||||||
Total | |
0.00% |
0 / 1 |
|
83.33% |
10 / 12 |
CRAP | |
90.82% |
89 / 98 |
Login | |
0.00% |
0 / 1 |
|
83.33% |
10 / 12 |
37.53 | |
92.71% |
89 / 96 |
__construct | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
name | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
init | |
100.00% |
1 / 1 |
1 | |
100.00% |
10 / 10 |
|||
login_button | |
100.00% |
1 / 1 |
1 | |
100.00% |
7 / 7 |
|||
authenticate | |
0.00% |
0 / 1 |
11 | |
95.24% |
20 / 21 |
|||
register | |
100.00% |
1 / 1 |
7 | |
100.00% |
17 / 17 |
|||
user_meta | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
redirect_url | |
100.00% |
1 / 1 |
1 | |
100.00% |
2 / 2 |
|||
state_redirect | |
100.00% |
1 / 1 |
1 | |
100.00% |
4 / 4 |
|||
login_redirect | |
0.00% |
0 / 1 |
21.82 | |
40.00% |
4 / 10 |
|||
user_login | |
100.00% |
1 / 1 |
3 | |
100.00% |
8 / 8 |
|||
can_register_with_email | |
100.00% |
1 / 1 |
1 | |
100.00% |
7 / 7 |
1 | <?php |
2 | /** |
3 | * Login class. |
4 | * |
5 | * This will manage the login flow, which includes adding the |
6 | * google login button on wp-login page, authorizing the user, |
7 | * authenticating user and redirecting him to admin. |
8 | * |
9 | * @package RtCamp\GoogleLogin |
10 | * @since 1.0.0 |
11 | */ |
12 | |
13 | declare(strict_types=1); |
14 | |
15 | namespace RtCamp\GoogleLogin\Modules; |
16 | |
17 | use stdClass; |
18 | use RtCamp\GoogleLogin\Utils\GoogleClient; |
19 | use RtCamp\GoogleLogin\Utils\Helper; |
20 | use WP_User; |
21 | use WP_Error; |
22 | use Exception; |
23 | use RtCamp\GoogleLogin\Interfaces\Module as ModuleInterface; |
24 | use function RtCamp\GoogleLogin\plugin; |
25 | |
26 | /** |
27 | * Class Login. |
28 | * |
29 | * @package RtCamp\GoogleLogin\Modules |
30 | */ |
31 | class Login implements ModuleInterface { |
32 | /** |
33 | * Google client instance. |
34 | * |
35 | * @var GoogleClient |
36 | */ |
37 | private $gh_client; |
38 | |
39 | /** |
40 | * Settings object. |
41 | * |
42 | * @var Settings |
43 | */ |
44 | private $settings; |
45 | |
46 | /** |
47 | * Flag for determining whether the user has been authenticated |
48 | * from plugin. |
49 | * |
50 | * @var bool |
51 | */ |
52 | private $authenticated = false; |
53 | |
54 | /** |
55 | * Login constructor. |
56 | * |
57 | * @param GoogleClient $client GH Client object. |
58 | * @param Settings $settings Settings object. |
59 | */ |
60 | public function __construct( GoogleClient $client, Settings $settings ) { |
61 | $this->gh_client = $client; |
62 | $this->settings = $settings; |
63 | } |
64 | |
65 | /** |
66 | * Module name. |
67 | * |
68 | * @return string |
69 | */ |
70 | public function name(): string { |
71 | return 'login_flow'; |
72 | } |
73 | |
74 | /** |
75 | * Initialize login flow. |
76 | * |
77 | * @return void |
78 | */ |
79 | public function init(): void { |
80 | add_action( 'login_form', [ $this, 'login_button' ] ); |
81 | add_action( 'authenticate', [ $this, 'authenticate' ] ); |
82 | add_action( 'rtcamp.google_register_user', [ $this, 'register' ] ); |
83 | add_action( 'rtcamp.google_redirect_url', [ $this, 'redirect_url' ] ); |
84 | add_action( 'rtcamp.google_user_created', [ $this, 'user_meta' ], 10, 2 ); |
85 | add_filter( 'rtcamp.google_user_profile', [ $this, 'user_login' ] ); |
86 | add_filter( 'rtcamp.google_login_state', [ $this, 'state_redirect' ] ); |
87 | add_action( 'wp_login', [ $this, 'login_redirect' ] ); |
88 | } |
89 | |
90 | /** |
91 | * Add the login button to login form. |
92 | * |
93 | * @return void |
94 | */ |
95 | public function login_button(): void { |
96 | $template = trailingslashit( plugin()->template_dir ) . 'google-login-button.php'; |
97 | $login_url = plugin()->container()->get( 'gh_client' )->authorization_url(); |
98 | |
99 | Helper::render_template( |
100 | $template, |
101 | [ |
102 | 'login_url' => $login_url, |
103 | ] |
104 | ); |
105 | } |
106 | |
107 | /** |
108 | * Authenticate the user. |
109 | * |
110 | * @param WP_User|null $user User object. Default is null. |
111 | * |
112 | * @return WP_User|WP_Error |
113 | */ |
114 | public function authenticate( $user = null ) { |
115 | if ( $user instanceof WP_User ) { |
116 | return $user; |
117 | } |
118 | |
119 | $code = Helper::filter_input( INPUT_GET, 'code', FILTER_SANITIZE_STRING ); |
120 | |
121 | if ( ! $code ) { |
122 | return $user; |
123 | } |
124 | |
125 | $state = Helper::filter_input( INPUT_GET, 'state', FILTER_SANITIZE_STRING ); |
126 | $decoded_state = $state ? (array) ( json_decode( base64_decode( $state ) ) ) : null; |
127 | |
128 | if ( ! is_array( $decoded_state ) || empty( $decoded_state['provider'] ) || 'google' !== $decoded_state['provider'] ) { |
129 | return $user; |
130 | } |
131 | |
132 | if ( empty( $decoded_state['nonce'] ) || ! wp_verify_nonce( $decoded_state['nonce'], 'login_with_google' ) ) { |
133 | return $user; |
134 | } |
135 | |
136 | try { |
137 | $this->gh_client->set_access_token( $code ); |
138 | $user = $this->gh_client->user(); |
139 | $user = apply_filters( 'rtcamp.google_user_profile', $user ); |
140 | |
141 | if ( email_exists( $user->email ) ) { |
142 | $this->authenticated = true; |
143 | return get_user_by( 'email', $user->email ); |
144 | } |
145 | |
146 | /** |
147 | * Check if we need to register the user. |
148 | * |
149 | * @param stdClass $user User object from google. |
150 | * @since 1.0.0 |
151 | */ |
152 | return apply_filters( 'rtcamp.google_register_user', $user ); |
153 | |
154 | } catch ( \Throwable $e ) { |
155 | return new WP_Error( 'google_login_failed', $e->getMessage() ); |
156 | } |
157 | } |
158 | |
159 | /** |
160 | * Register the new user if setting is on for registration. |
161 | * |
162 | * @param stdClass $user User object from google. |
163 | * |
164 | * @return WP_User|null |
165 | * @throws \Throwable Invalid email registration. |
166 | * @throws Exception Registration is off. |
167 | */ |
168 | public function register( stdClass $user ): ?WP_User { |
169 | $register = true === (bool) $this->settings->registration_enabled || (bool) get_option( 'users_can_register', false ); |
170 | |
171 | if ( ! $register ) { |
172 | throw new Exception( __( 'Registration is not allowed.', 'login-with-google' ) ); |
173 | } |
174 | |
175 | try { |
176 | $whitelisted_domains = $this->settings->whitelisted_domains; |
177 | if ( empty( $whitelisted_domains ) || $this->can_register_with_email( $user->email ) ) { |
178 | $uid = wp_insert_user( |
179 | [ |
180 | 'user_login' => Helper::unique_username( $user->login ), |
181 | 'user_pass' => wp_generate_password( 18 ), |
182 | 'user_email' => $user->email, |
183 | ] |
184 | ); |
185 | |
186 | if ( $uid ) { |
187 | $this->authenticated = true; |
188 | } |
189 | |
190 | /** |
191 | * Fires once the user has been registered successfully. |
192 | */ |
193 | do_action( 'rtcamp.google_user_created', $uid, $user ); |
194 | |
195 | return get_user_by( 'id', $uid ); |
196 | } |
197 | |
198 | /* translators: %s is replaced with email ID of user trying to register */ |
199 | throw new Exception( sprintf( __( 'Cannot register with this email: %s', 'login-with-google' ), $user->email ) ); |
200 | |
201 | } catch ( \Throwable $e ) { |
202 | |
203 | throw $e; |
204 | } |
205 | |
206 | } |
207 | |
208 | /** |
209 | * Add extra meta information about user. |
210 | * |
211 | * @param int $uid User ID. |
212 | * @param stdClass $user User object. |
213 | * |
214 | * @return void |
215 | */ |
216 | public function user_meta( int $uid, stdClass $user ) { |
217 | add_user_meta( $uid, 'oauth_user', 1, true ); |
218 | add_user_meta( $uid, 'oauth_provider', 'google', true ); |
219 | } |
220 | |
221 | /** |
222 | * Redirect URL. |
223 | * |
224 | * This is useful when redirect URL is present when |
225 | * trying to login to wp-admin. |
226 | * |
227 | * @param string $url Redirect URL address. |
228 | * |
229 | * @return string |
230 | */ |
231 | public function redirect_url( string $url ): string { |
232 | |
233 | return remove_query_arg( 'redirect_to', $url ); |
234 | } |
235 | |
236 | /** |
237 | * Add redirect_to location in state. |
238 | * |
239 | * @param array $state State data. |
240 | * |
241 | * @return array |
242 | */ |
243 | public function state_redirect( array $state ): array { |
244 | $redirect_to = Helper::filter_input( INPUT_GET, 'redirect_to', FILTER_SANITIZE_STRING ); |
245 | /** |
246 | * Filter the default redirect URL in case redirect_to param is not available. |
247 | * Default to admin URL. |
248 | * |
249 | * @param string $admin_url Admin URL address. |
250 | */ |
251 | $state['redirect_to'] = $redirect_to ?? apply_filters( 'rtcamp.google_default_redirect', admin_url() ); |
252 | |
253 | return $state; |
254 | } |
255 | |
256 | /** |
257 | * Add a redirect once user has been authenticated successfully. |
258 | * |
259 | * @return void |
260 | */ |
261 | public function login_redirect(): void { |
262 | $state = Helper::filter_input( INPUT_GET, 'state', FILTER_SANITIZE_STRING ); |
263 | |
264 | if ( ! $state || ! $this->authenticated ) { |
265 | return; |
266 | } |
267 | |
268 | $state = base64_decode( $state ); |
269 | $state = $state ? json_decode( $state ) : null; |
270 | |
271 | if ( ( $state instanceof stdClass ) && ! empty( $state->provider ) && 'google' === $state->provider && ! empty ( $state->redirect_to ) ) { |
272 | wp_safe_redirect( $state->redirect_to ); |
273 | exit; |
274 | } |
275 | } |
276 | |
277 | /** |
278 | * Assign the `login` property to user object |
279 | * if it doesn't exists. |
280 | * |
281 | * @param stdClass $user User object. |
282 | * |
283 | * @return stdClass |
284 | */ |
285 | public function user_login( stdClass $user ): stdClass { |
286 | if ( property_exists( $user, 'login' ) || ! property_exists( $user, 'email' ) ) { |
287 | return $user; |
288 | } |
289 | |
290 | $email = $user->email; |
291 | $user_login = sanitize_user( current( explode( '@', $email ) ), true ); |
292 | $user_login = Helper::unique_username( $user_login ); |
293 | $user->login = $user_login; |
294 | |
295 | return $user; |
296 | } |
297 | |
298 | /** |
299 | * Check if given email can be used for registration. |
300 | * |
301 | * @param string $email Email ID. |
302 | * |
303 | * @return bool |
304 | */ |
305 | private function can_register_with_email( string $email ): bool { |
306 | $whitelisted_domains = explode( ',', $this->settings->whitelisted_domains ); |
307 | $whitelisted_domains = array_map( 'strtolower', $whitelisted_domains ); |
308 | $whitelisted_domains = array_map( 'trim', $whitelisted_domains ); |
309 | $email_parts = explode( '@', $email ); |
310 | $email_parts = array_map( 'strtolower', $email_parts ); |
311 | |
312 | return in_array( $email_parts[1], $whitelisted_domains, true ); |
313 | } |
314 | } |