class.jetpack-sso-helpers.php 9.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325
  1. <?php
  2. if ( ! class_exists( 'Jetpack_SSO_Helpers' ) ) :
  3. /**
  4. * A collection of helper functions used in the SSO module.
  5. *
  6. * @since 4.1.0
  7. */
  8. class Jetpack_SSO_Helpers {
  9. /**
  10. * Determine if the login form should be hidden or not
  11. *
  12. * @return bool
  13. **/
  14. static function should_hide_login_form() {
  15. /**
  16. * Remove the default log in form, only leave the WordPress.com log in button.
  17. *
  18. * @module sso
  19. *
  20. * @since 3.1.0
  21. *
  22. * @param bool get_option( 'jetpack_sso_remove_login_form', false ) Should the default log in form be removed. Default to false.
  23. */
  24. return (bool) apply_filters( 'jetpack_remove_login_form', get_option( 'jetpack_sso_remove_login_form', false ) );
  25. }
  26. /**
  27. * Returns a boolean value for whether logging in by matching the WordPress.com user email to a
  28. * Jetpack site user's email is allowed.
  29. *
  30. * @return bool
  31. */
  32. static function match_by_email() {
  33. $match_by_email = ( 1 == get_option( 'jetpack_sso_match_by_email', true ) ) ? true: false;
  34. $match_by_email = defined( 'WPCC_MATCH_BY_EMAIL' ) ? WPCC_MATCH_BY_EMAIL : $match_by_email;
  35. /**
  36. * Link the local account to an account on WordPress.com using the same email address.
  37. *
  38. * @module sso
  39. *
  40. * @since 2.6.0
  41. *
  42. * @param bool $match_by_email Should we link the local account to an account on WordPress.com using the same email address. Default to false.
  43. */
  44. return (bool) apply_filters( 'jetpack_sso_match_by_email', $match_by_email );
  45. }
  46. /**
  47. * Returns a boolean for whether users are allowed to register on the Jetpack site with SSO,
  48. * even though the site disallows normal registrations.
  49. *
  50. * @return bool
  51. */
  52. static function new_user_override( $user_data = null ) {
  53. $new_user_override = defined( 'WPCC_NEW_USER_OVERRIDE' ) ? WPCC_NEW_USER_OVERRIDE : false;
  54. /**
  55. * Allow users to register on your site with a WordPress.com account, even though you disallow normal registrations.
  56. * If you return a string that corresponds to a user role, the user will be given that role.
  57. *
  58. * @module sso
  59. *
  60. * @since 2.6.0
  61. * @since 4.6 $user_data object is now passed to the jetpack_sso_new_user_override filter
  62. *
  63. * @param bool $new_user_override Allow users to register on your site with a WordPress.com account. Default to false.
  64. * @param object|null $user_data An object containing the user data returned from WordPress.com.
  65. */
  66. $role = apply_filters( 'jetpack_sso_new_user_override', $new_user_override, $user_data );
  67. if ( $role ) {
  68. if ( is_string( $role ) && get_role( $role ) ) {
  69. return $role;
  70. } else {
  71. return get_option( 'default_role' );
  72. }
  73. }
  74. return false;
  75. }
  76. /**
  77. * Returns a boolean value for whether two-step authentication is required for SSO.
  78. *
  79. * @since 4.1.0
  80. *
  81. * @return bool
  82. */
  83. static function is_two_step_required() {
  84. /**
  85. * Is it required to have 2-step authentication enabled on WordPress.com to use SSO?
  86. *
  87. * @module sso
  88. *
  89. * @since 2.8.0
  90. *
  91. * @param bool get_option( 'jetpack_sso_require_two_step' ) Does SSO require 2-step authentication?
  92. */
  93. return (bool) apply_filters( 'jetpack_sso_require_two_step', get_option( 'jetpack_sso_require_two_step', false ) );
  94. }
  95. /**
  96. * Returns a boolean for whether a user that is attempting to log in will be automatically
  97. * redirected to WordPress.com to begin the SSO flow.
  98. *
  99. * @return bool
  100. */
  101. static function bypass_login_forward_wpcom() {
  102. /**
  103. * Redirect the site's log in form to WordPress.com's log in form.
  104. *
  105. * @module sso
  106. *
  107. * @since 3.1.0
  108. *
  109. * @param bool false Should the site's log in form be automatically forwarded to WordPress.com's log in form.
  110. */
  111. return (bool) apply_filters( 'jetpack_sso_bypass_login_forward_wpcom', false );
  112. }
  113. /**
  114. * Returns a boolean for whether the SSO login form should be displayed as the default
  115. * when both the default and SSO login form allowed.
  116. *
  117. * @since 4.1.0
  118. *
  119. * @return bool
  120. */
  121. static function show_sso_login() {
  122. if ( self::should_hide_login_form() ) {
  123. return true;
  124. }
  125. /**
  126. * Display the SSO login form as the default when both the default and SSO login forms are enabled.
  127. *
  128. * @module sso
  129. *
  130. * @since 4.1.0
  131. *
  132. * @param bool true Should the SSO login form be displayed by default when the default login form is also enabled?
  133. */
  134. return (bool) apply_filters( 'jetpack_sso_default_to_sso_login', true );
  135. }
  136. /**
  137. * Returns a boolean for whether the two step required checkbox, displayed on the Jetpack admin page, should be disabled.
  138. *
  139. * @since 4.1.0
  140. *
  141. * @return bool
  142. */
  143. static function is_require_two_step_checkbox_disabled() {
  144. return (bool) has_filter( 'jetpack_sso_require_two_step' );
  145. }
  146. /**
  147. * Returns a boolean for whether the match by email checkbox, displayed on the Jetpack admin page, should be disabled.
  148. *
  149. * @since 4.1.0
  150. *
  151. * @return bool
  152. */
  153. static function is_match_by_email_checkbox_disabled() {
  154. return defined( 'WPCC_MATCH_BY_EMAIL' ) || has_filter( 'jetpack_sso_match_by_email' );
  155. }
  156. /**
  157. * Returns an array of hosts that SSO will redirect to.
  158. *
  159. * Instead of accessing JETPACK__API_BASE within the method directly, we set it as the
  160. * default for $api_base due to restrictions with testing constants in our tests.
  161. *
  162. * @since 4.3.0
  163. * @since 4.6.0 Added public-api.wordpress.com as an allowed redirect
  164. *
  165. * @param array $hosts
  166. * @param string $api_base
  167. *
  168. * @return array
  169. */
  170. static function allowed_redirect_hosts( $hosts, $api_base = JETPACK__API_BASE ) {
  171. if ( empty( $hosts ) ) {
  172. $hosts = array();
  173. }
  174. $hosts[] = 'wordpress.com';
  175. $hosts[] = 'jetpack.wordpress.com';
  176. $hosts[] = 'public-api.wordpress.com';
  177. if ( false === strpos( $api_base, 'jetpack.wordpress.com/jetpack' ) ) {
  178. $base_url_parts = parse_url( esc_url_raw( $api_base ) );
  179. if ( $base_url_parts && ! empty( $base_url_parts[ 'host' ] ) ) {
  180. $hosts[] = $base_url_parts[ 'host' ];
  181. }
  182. }
  183. return array_unique( $hosts );
  184. }
  185. static function generate_user( $user_data ) {
  186. $username = $user_data->login;
  187. /**
  188. * Determines how many times the SSO module can attempt to randomly generate a user.
  189. *
  190. * @module sso
  191. *
  192. * @since 4.3.2
  193. *
  194. * @param int 5 By default, SSO will attempt to random generate a user up to 5 times.
  195. */
  196. $num_tries = intval( apply_filters( 'jetpack_sso_allowed_username_generate_retries', 5 ) );
  197. $tries = 0;
  198. while ( ( $exists = username_exists( $username ) ) && $tries++ < $num_tries ) {
  199. $username = $user_data->login . '_' . $user_data->ID . '_' . mt_rand();
  200. }
  201. if ( $exists ) {
  202. return false;
  203. }
  204. $user = (object) array();
  205. $user->user_pass = wp_generate_password( 20 );
  206. $user->user_login = wp_slash( $username );
  207. $user->user_email = wp_slash( $user_data->email );
  208. $user->display_name = $user_data->display_name;
  209. $user->first_name = $user_data->first_name;
  210. $user->last_name = $user_data->last_name;
  211. $user->url = $user_data->url;
  212. $user->description = $user_data->description;
  213. if ( isset( $user_data->role ) && $user_data->role ) {
  214. $user->role = $user_data->role;
  215. }
  216. $created_user_id = wp_insert_user( $user );
  217. update_user_meta( $created_user_id, 'wpcom_user_id', $user_data->ID );
  218. return get_userdata( $created_user_id );
  219. }
  220. static function extend_auth_cookie_expiration_for_sso() {
  221. /**
  222. * Determines how long the auth cookie is valid for when a user logs in with SSO.
  223. *
  224. * @module sso
  225. *
  226. * @since 4.4.0
  227. * @since 6.1.0 Fixed a typo. Filter was previously jetpack_sso_auth_cookie_expirtation.
  228. *
  229. * @param int YEAR_IN_SECONDS
  230. */
  231. return intval( apply_filters( 'jetpack_sso_auth_cookie_expiration', YEAR_IN_SECONDS ) );
  232. }
  233. /**
  234. * Determines if the SSO form should be displayed for the current action.
  235. *
  236. * @since 4.6.0
  237. *
  238. * @param string $action
  239. *
  240. * @return bool Is SSO allowed for the current action?
  241. */
  242. static function display_sso_form_for_action( $action ) {
  243. /**
  244. * Allows plugins the ability to overwrite actions where the SSO form is allowed to be used.
  245. *
  246. * @module sso
  247. *
  248. * @since 4.6.0
  249. *
  250. * @param array $allowed_actions_for_sso
  251. */
  252. $allowed_actions_for_sso = (array) apply_filters( 'jetpack_sso_allowed_actions', array(
  253. 'login',
  254. 'jetpack-sso',
  255. 'jetpack_json_api_authorization',
  256. ) );
  257. return in_array( $action, $allowed_actions_for_sso );
  258. }
  259. /**
  260. * This method returns an environment array that is meant to simulate `$_REQUEST` when the initial
  261. * JSON API auth request was made.
  262. *
  263. * @since 4.6.0
  264. *
  265. * @return array|bool
  266. */
  267. static function get_json_api_auth_environment() {
  268. if ( empty( $_COOKIE['jetpack_sso_original_request'] ) ) {
  269. return false;
  270. }
  271. $original_request = esc_url_raw( $_COOKIE['jetpack_sso_original_request'] );
  272. $parsed_url = wp_parse_url( $original_request );
  273. if ( empty( $parsed_url ) || empty( $parsed_url['query'] ) ) {
  274. return false;
  275. }
  276. $args = array();
  277. wp_parse_str( $parsed_url['query'], $args );
  278. if ( empty( $args ) || empty( $args['action'] ) ) {
  279. return false;
  280. }
  281. if ( 'jetpack_json_api_authorization' != $args['action'] ) {
  282. return false;
  283. }
  284. return array_merge(
  285. $args,
  286. array( 'jetpack_json_api_original_query' => $original_request )
  287. );
  288. }
  289. }
  290. endif;