From c6460784212fc3d8f92f4921f7037b5f4e7cace8 Mon Sep 17 00:00:00 2001
From: Jonathan Daggerhart
Date: Mon, 21 Sep 2015 10:16:31 -0400
Subject: [PATCH] refactoring some internal settings for clarity
---
.../openid-connect-generic-client-wrapper.php | 153 ++++++++----------
includes/openid-connect-generic-client.php | 2 +-
.../openid-connect-generic-settings-page.php | 83 +++++-----
includes/wp-option-logger.php | 20 ++-
includes/wp-option-settings.php | 8 +-
openid-connect-generic.php | 58 +++++--
6 files changed, 168 insertions(+), 156 deletions(-)
diff --git a/includes/openid-connect-generic-client-wrapper.php b/includes/openid-connect-generic-client-wrapper.php
index 70b39d2..41641ee 100644
--- a/includes/openid-connect-generic-client-wrapper.php
+++ b/includes/openid-connect-generic-client-wrapper.php
@@ -55,21 +55,12 @@ class OpenID_Connect_Generic_Client_Wrapper {
add_action( 'wp_ajax_nopriv_openid-connect-authorize', array( $client_wrapper, 'authentication_request_callback' ) );
}
- $client_wrapper->startup();
-
- return $client_wrapper;
- }
-
- /**
- * Handle the initial validation that should occur on each page load
- */
- function startup(){
- $this->handle_privacy();
-
// verify token for any logged in user
if ( is_user_logged_in() ) {
- $this->check_user_token();
+ $client_wrapper->check_user_token();
}
+
+ return $client_wrapper;
}
/**
@@ -80,26 +71,6 @@ class OpenID_Connect_Generic_Client_Wrapper {
function get_authentication_url(){
return $this->client->make_authentication_url();
}
-
- /**
- * Handle the privacy settings
- */
- function handle_privacy() {
- // check if privacy enforcement is enabled
- if ( $this->settings->enforce_privacy &&
- ! is_user_logged_in() &&
- // avoid redirects on cron or ajax
- ( ! defined( 'DOING_AJAX' ) || ! DOING_AJAX ) &&
- ( ! defined( 'DOING_CRON' ) || ! DOING_CRON )
- ) {
- global $pagenow;
-
- // avoid redirect loop
- if ( $pagenow != 'wp-login.php' && ! isset( $_GET['loggedout'] ) && ! isset( $_GET['login-error'] ) ) {
- $this->error_redirect( new WP_Error( 'privacy', __( 'This site requires login.' ), $_GET ) );
- }
- }
- }
/**
* Check the user's cookie
@@ -146,7 +117,7 @@ class OpenID_Connect_Generic_Client_Wrapper {
* Remove cookies
*/
function wp_logout() {
- setcookie( $this->cookie_id_key, '1', 0, COOKIEPATH, COOKIE_DOMAIN, TRUE );
+ setcookie( $this->cookie_id_key, false, 0, COOKIEPATH, COOKIE_DOMAIN, TRUE );
}
/**
@@ -173,7 +144,7 @@ class OpenID_Connect_Generic_Client_Wrapper {
$settings = $this->settings;
$client = $this->client;
- //
+ // start the authentication flow
$authentication_request = $client->validate_authentication_request( $_GET );
if ( is_wp_error( $authentication_request ) ){
@@ -207,79 +178,69 @@ class OpenID_Connect_Generic_Client_Wrapper {
if ( is_wp_error( $valid ) ) {
$this->error_redirect( $valid );
}
-
- // - end authentication
-
- // - start authorization
+ /**
+ * End authentication
+ * -
+ * Start Authorization
+ */
// The id_token is used to identify the authenticated user, e.g. for SSO.
// The access_token must be used to prove access rights to protected resources
// e.g. for the userinfo endpoint
-
- //
$id_token_claim = $client->get_id_token_claim( $token_response );
if ( is_wp_error( $id_token_claim ) ){
$this->error_redirect( $id_token_claim );
}
- //
+ // validate our id_token has required values
$valid = $client->validate_id_token_claim( $id_token_claim );
if ( is_wp_error( $valid ) ){
$this->error_redirect( $valid );
}
-
-
-//
-// // if desired, admins can use regex to determine if the identity value is valid
-// // according to their own standards expectations
-// if ( ! empty( $settings->allowed_regex ) &&
-// preg_match( $settings->allowed_regex, $id_token_claim[ $settings->identity_key ] ) !== 1
-// ) {
-// return new WP_Error( 'no-subject-identity', __( 'No subject identity' ), $id_token_claim );
-// }
-
- //
+ // exchange the token_response for a user_claim
$user_claim = $client->get_user_claim( $token_response );
if ( is_wp_error( $user_claim ) ){
$this->error_redirect( $user_claim );
}
- //
+ // validate our user_claim has required values
$valid = $client->validate_user_claim( $user_claim, $id_token_claim );
if ( is_wp_error( $valid ) ){
$this->error_redirect( $valid );
}
- // - end authorization
-
-
-
- // request is authenticated and authorized
- // - start user handling
- $user_identity = $client->get_user_identity( $id_token_claim );
- $user = $this->get_user_by_identity( $user_identity );
+ /**
+ * End authorization
+ * -
+ * Request is authenticated and authorized - start user handling
+ */
+ $subject_identity = $client->get_subject_identity( $id_token_claim );
+ $user = $this->get_user_by_identity( $subject_identity );
// if we didn't find an existing user, we'll need to create it
if ( ! $user ) {
- $user = $this->create_new_user( $user_identity, $user_claim );
+ $user = $this->create_new_user( $subject_identity, $user_claim );
}
- //
+ // validate the found / created user
$valid = $this->validate_user( $user );
if ( is_wp_error( $valid ) ){
$this->error_redirect( $valid );
}
- $this->login_user( $user, $token_response, $id_token_claim, $user_claim, $user_identity );
+ // login the found / created user
+ $this->login_user( $user, $token_response, $id_token_claim, $user_claim, $subject_identity );
+ // log our success
$this->logger->log( "Successful login for: {$user->user_login} ({$user->ID})", 'login-success' );
+ // go home!
wp_redirect( home_url() );
}
@@ -300,11 +261,11 @@ class OpenID_Connect_Generic_Client_Wrapper {
}
/**
- *
+ * Record user meta data, and provide an authorization cookie
*
* @param $user
*/
- function login_user( $user, $token_response, $id_token_claim, $user_claim, $user_identity ){
+ function login_user( $user, $token_response, $id_token_claim, $user_claim, $subject_identity ){
// hey, we made it!
// let's remember the tokens for future reference
update_user_meta( $user->ID, 'openid-connect-generic-last-token-response', $token_response );
@@ -313,26 +274,26 @@ class OpenID_Connect_Generic_Client_Wrapper {
// save our authorization cookie for the response expiration
$oauth_expiry = $token_response['expires_in'] + current_time( 'timestamp', TRUE );
- setcookie( $this->cookie_id_key, $user_identity, $oauth_expiry, COOKIEPATH, COOKIE_DOMAIN, TRUE );
+ setcookie( $this->cookie_id_key, $subject_identity, $oauth_expiry, COOKIEPATH, COOKIE_DOMAIN, TRUE );
- // get a cookie and go home!
+ // you did great, have a cookie!
wp_set_auth_cookie( $user->ID, FALSE );
}
/**
+ * Get the user that has meta data matching a
*
- *
- * @param $user_identity
+ * @param $subject_identity
*
* @return false|\WP_User
*/
- function get_user_by_identity( $user_identity ){
- // look for user by their openid-connect-generic-user-identity value
+ function get_user_by_identity( $subject_identity ){
+ // look for user by their openid-connect-generic-subject-identity value
$user_query = new WP_User_Query( array(
'meta_query' => array(
array(
- 'key' => 'openid-connect-generic-user-identity',
- 'value' => $user_identity,
+ 'key' => 'openid-connect-generic-subject-identity',
+ 'value' => $subject_identity,
)
)
) );
@@ -354,7 +315,11 @@ class OpenID_Connect_Generic_Client_Wrapper {
* @return string
*/
private function get_username_from_claim( $user_claim ) {
- if ( isset( $user_claim['preferred_username'] ) && ! empty( $user_claim['preferred_username'] ) ) {
+ // allow settings to take first stab at username
+ if ( !empty( $this->settings->identity_key ) && isset( $user_claim[ $this->settings->identity_key ] ) ) {
+ $desired_username = $user_claim[ $this->settings->identity_key ];
+ }
+ else if ( isset( $user_claim['preferred_username'] ) && ! empty( $user_claim['preferred_username'] ) ) {
$desired_username = $user_claim['preferred_username'];
}
else if ( isset( $user_claim['name'] ) && ! empty( $user_claim['name'] ) ) {
@@ -388,25 +353,28 @@ class OpenID_Connect_Generic_Client_Wrapper {
}
/**
+ * Create a new user from details in a user_claim
*
- *
- * @param $user_identity
+ * @param $subject_identity
* @param $user_claim
*
* @return \WP_Error | \WP_User
*/
- function create_new_user( $user_identity, $user_claim){
- // default username & email to the user identity, since that is the only
- // thing we can be sure to have
- $username = $user_identity;
- $email = $user_identity;
+ function create_new_user( $subject_identity, $user_claim){
+ // default username & email to the subject identity
+ $username = $subject_identity;
+ $email = $subject_identity;
// allow claim details to determine username
if ( isset( $user_claim['email'] ) ) {
$email = $user_claim['email'];
$username = $this->get_username_from_claim( $user_claim );
+
+ if ( is_wp_error( $username ) ){
+ return $username;
+ }
}
- // if no name exists, attempt another request for userinfo
+ // if no email exists, attempt another request for userinfo
else if ( isset( $token_response['access_token'] ) ) {
$user_claim_result = $this->client->request_userinfo( $token_response['access_token'] );
@@ -417,10 +385,13 @@ class OpenID_Connect_Generic_Client_Wrapper {
$user_claim = json_decode( $user_claim_result['body'], TRUE );
- if ( isset( $user_claim['email'] ) ) {
- $email = $user_claim['email'];
- $username = $this->get_username_from_claim( $user_claim );
+ // check for email in claim
+ if ( ! isset( $user_claim['email'] ) ) {
+ return new WP_Error( 'incomplete-user-claim', __( 'User claim incomplete' ), $user_claim );
}
+
+ $email = $user_claim['email'];
+ $username = $this->get_username_from_claim( $user_claim );
}
// allow other plugins / themes to determine authorization
@@ -439,13 +410,15 @@ class OpenID_Connect_Generic_Client_Wrapper {
return new WP_Error( 'failed-user-creation', __( 'Failed user creation.' ), $uid );
}
+ // retrieve our new user
$user = get_user_by( 'id', $uid );
- $this->log( "New user created: {$user->user_login} ($uid)", 'success' );
-
// save some meta data about this new user for the future
add_user_meta( $user->ID, 'openid-connect-generic-user', TRUE, TRUE );
- add_user_meta( $user->ID, 'openid-connect-generic-user-identity', (string) $user_identity, TRUE );
+ add_user_meta( $user->ID, 'openid-connect-generic-subject-identity', (string) $subject_identity, TRUE );
+
+ // log the results
+ $this->logger->log( "New user created: {$user->user_login} ($uid)", 'success' );
// allow plugins / themes to take action on new user creation
do_action( 'openid-connect-generic-user-create', $user, $user_claim );
diff --git a/includes/openid-connect-generic-client.php b/includes/openid-connect-generic-client.php
index c93cd63..da3a4fd 100644
--- a/includes/openid-connect-generic-client.php
+++ b/includes/openid-connect-generic-client.php
@@ -317,7 +317,7 @@ class OpenID_Connect_Generic_Client {
*
* @return mixed
*/
- function get_user_identity( $id_token_claim ){
+ function get_subject_identity( $id_token_claim ){
return $id_token_claim['sub'];
}
}
\ No newline at end of file
diff --git a/includes/openid-connect-generic-settings-page.php b/includes/openid-connect-generic-settings-page.php
index 0b1a220..cc88057 100644
--- a/includes/openid-connect-generic-settings-page.php
+++ b/includes/openid-connect-generic-settings-page.php
@@ -34,7 +34,7 @@ class OpenID_Connect_Generic_Settings_Page {
* - example (optional example will appear beneath description and be wrapped in )
*/
$fields = array(
- 'login_type' => array(
+ 'login_type' => array(
'title' => __( 'Login Type' ),
'description' => __( 'Select how the client (login form) should provide login options.' ),
'type' => 'select',
@@ -44,79 +44,73 @@ class OpenID_Connect_Generic_Settings_Page {
),
'section' => 'client_settings',
),
- 'ep_login' => array(
- 'title' => __( 'Login Endpoint URL' ),
- 'description' => __( 'Identify provider authorization endpoint.' ),
- 'example' => 'https://example.com/oauth2/authorize',
+ 'client_id' => array(
+ 'title' => __( 'Client ID' ),
+ 'description' => __( 'The ID this client will be recognized as when connecting the to Identity provider server.' ),
+ 'example' => 'my-wordpress-client-id',
'type' => 'text',
'section' => 'client_settings',
),
- 'ep_token' => array(
- 'title' => __( 'Token Validation Endpoint URL' ),
- 'description' => __( 'Identify provider token endpoint.' ),
- 'example' => 'https://example.com/oauth2/token',
+ 'client_secret' => array(
+ 'title' => __( 'Client Secret Key' ),
+ 'description' => __( 'Arbitrary secret key the server expects from this client. Can be anything, but should be very unique.' ),
'type' => 'text',
'section' => 'client_settings',
),
- 'ep_userinfo' => array(
- 'title' => __( 'Userinfo Endpoint URL' ),
- 'description' => __( 'Identify provider User information endpoint.' ),
- 'example' => 'https://example.com/oauth2/UserInfo',
+ 'scope' => array(
+ 'title' => __( 'OpenID Scope' ),
+ 'description' => __( 'Space separated list of scopes this client should access.' ),
+ 'example' => 'email profile openid',
'type' => 'text',
'section' => 'client_settings',
),
- 'no_sslverify' => array(
- 'title' => __( 'Disable SSL Verify' ),
- 'description' => __( 'Do not require SSL verification during authorization. The OAuth extension uses curl to make the request. By default CURL will generally verify the SSL certificate to see if its valid an issued by an accepted CA. This setting disabled that verification.' ),
- 'type' => 'checkbox',
+ 'endpoint_login' => array(
+ 'title' => __( 'Login Endpoint URL' ),
+ 'description' => __( 'Identify provider authorization endpoint.' ),
+ 'example' => 'https://example.com/oauth2/authorize',
+ 'type' => 'text',
'section' => 'client_settings',
),
- 'client_id' => array(
- 'title' => __( 'Client ID' ),
- 'description' => __( 'The ID this client will be recognized as when connecting the to Identity provider server.' ),
- 'example' => 'my-wordpress-client-id',
+ 'endpoint_userinfo' => array(
+ 'title' => __( 'Userinfo Endpoint URL' ),
+ 'description' => __( 'Identify provider User information endpoint.' ),
+ 'example' => 'https://example.com/oauth2/UserInfo',
'type' => 'text',
'section' => 'client_settings',
),
- 'client_secret' => array(
- 'title' => __( 'Client Secret Key' ),
- 'description' => __( 'Arbitrary secret key the server expects from this client. Can be anything, but should be very unique.' ),
+ 'endpoint_token' => array(
+ 'title' => __( 'Token Validation Endpoint URL' ),
+ 'description' => __( 'Identify provider token endpoint.' ),
+ 'example' => 'https://example.com/oauth2/token',
'type' => 'text',
'section' => 'client_settings',
),
- 'scope' => array(
- 'title' => __( 'OpenID Scope' ),
- 'description' => __( 'Space separated list of scopes this client should access.' ),
- 'example' => 'email profile openid',
- 'type' => 'text',
+ 'no_sslverify' => array(
+ 'title' => __( 'Disable SSL Verify' ),
+ 'description' => __( 'Do not require SSL verification during authorization. The OAuth extension uses curl to make the request. By default CURL will generally verify the SSL certificate to see if its valid an issued by an accepted CA. This setting disabled that verification.' ),
+ 'type' => 'checkbox',
'section' => 'client_settings',
),
- 'identity_key' => array(
+ 'identity_key' => array(
'title' => __( 'Identity Key' ),
- 'description' => __( 'Where in the response array to find the identification data. When in doubt, use "sub".' ),
- 'example' => 'sub',
+ 'description' => __( 'Where in the user claim array to find the user\'s identification data. Possible standard values: preferred_username, name, or sub. If you\'re having trouble, use "sub".' ),
+ 'example' => 'preferred_username',
'type' => 'text',
'section' => 'client_settings',
),
- 'allowed_regex' => array(
- 'title' => __( 'Authorization Regex' ),
- 'description' => __( 'Provide a regular expression that enforces your expectations concerning the identity value returned from the IDP.' ),
- 'type' => 'text',
- 'section' => 'authorization_settings',
- ),
- 'enforce_privacy' => array(
+ 'enforce_privacy' => array(
'title' => __( 'Enforce Privacy' ),
'description' => __( 'Require users be logged in to see the site.' ),
'type' => 'checkbox',
'section' => 'authorization_settings',
),
- 'enable_logging' => array(
+ 'enable_logging' => array(
'title' => __( 'Enable Logging' ),
'description' => __( 'Very simple log messages for debugging purposes.' ),
'type' => 'checkbox',
'section' => 'log_settings',
),
- 'log_limit' => array(
+ 'log_limit' => array(
'title' => __( 'Log Limit' ),
'description' => __( 'Number of items to keep in the log. These logs are stored as an option in the database, so space is limited.' ),
'type' => 'number',
@@ -273,9 +267,10 @@ class OpenID_Connect_Generic_Settings_Page {
- show_logs();
- ?>
+ settings->enable_logging ) { ?>
+
+ logger->get_logs_table(); ?>
+
get_logs();
}
+ ini_set( 'xdebug.var_display_max_depth', -1 );
+
ob_start();
?>
-
+
+
- Details |
- Data |
+ Details |
+ Data |
-
+ |
@@ -204,11 +211,12 @@ class WP_Option_Logger {
user_login : '0'; ?>
-
+
|
- |
+
+ |
diff --git a/includes/wp-option-settings.php b/includes/wp-option-settings.php
index 53501e7..9fe1a04 100644
--- a/includes/wp-option-settings.php
+++ b/includes/wp-option-settings.php
@@ -8,11 +8,15 @@ class WP_Option_Settings {
private $option_name;
// stored option values array
- private $values = array();
+ private $values;
// default plugin settings values
private $default_settings;
-
+
+ /**
+ * @param $option_name
+ * @param array $default_settings
+ */
function __construct( $option_name, $default_settings = array() ){
$this->option_name = $option_name;
$this->default_settings = $default_settings;
diff --git a/openid-connect-generic.php b/openid-connect-generic.php
index f27dae5..5a25584 100644
--- a/openid-connect-generic.php
+++ b/openid-connect-generic.php
@@ -73,9 +73,9 @@ class OpenID_Connect_Generic {
$this->settings->client_id,
$this->settings->client_secret,
$this->settings->scope,
- $this->settings->ep_login,
- $this->settings->ep_userinfo,
- $this->settings->ep_token,
+ $this->settings->endpoint_login,
+ $this->settings->endpoint_userinfo,
+ $this->settings->endpoint_token,
// redirect uri
admin_url( 'admin-ajax.php?action=openid-connect-authorize' )
);
@@ -87,6 +87,33 @@ class OpenID_Connect_Generic {
$this->settings_page = OpenID_Connect_Generic_Settings_Page::register( $this->settings, $this->logger );
}
}
+
+ /**
+ * Check if privacy enforcement is enabled, and redirect users that aren't
+ * logged in.
+ */
+ function enforce_privacy_redirect() {
+ if ( $this->settings->enforce_privacy && ! is_user_logged_in() ) {
+ // our client endpoint relies on the wp admind ajax endpoint
+ if ( ! defined( 'DOING_AJAX') || ! DOING_AJAX || ! isset( $_GET['action'] ) || $_GET['action'] != 'openid-connect-authorize' ) {
+ auth_redirect();
+ }
+ }
+ }
+
+ /**
+ * Enforce privacy settings for rss feeds
+ *
+ * @param $content
+ *
+ * @return mixed
+ */
+ function enforce_privacy_feeds( $content ){
+ if ( $this->settings->enforce_privacy && ! is_user_logged_in() ) {
+ $content = 'Private site';
+ }
+ return $content;
+ }
/**
* Autoloader
@@ -112,20 +139,19 @@ class OpenID_Connect_Generic {
// default settings values
array(
// oauth client settings
- 'client_id' => '',
- 'client_secret' => '',
- 'scope' => '',
- 'ep_login' => '',
- 'ep_userinfo' => '',
- 'ep_token' => '',
+ 'login_type' => 'button',
+ 'client_id' => '',
+ 'client_secret' => '',
+ 'scope' => '',
+ 'endpoint_login' => '',
+ 'endpoint_userinfo' => '',
+ 'endpoint_token' => '',
// non-standard settings
'no_sslverify' => 0,
- 'identity_key' => 'sub',
- 'allowed_regex' => '',
-
+ 'identity_key' => 'preferred_username',
+
// plugin settings
- 'login_type' => 'button',
'enforce_privacy' => 0,
'enable_logging' => 0,
'log_limit' => 1000,
@@ -137,6 +163,12 @@ class OpenID_Connect_Generic {
$plugin = new self( $settings, $logger );
add_action( 'init', array( $plugin, 'init' ) );
+
+ // privacy hooks
+ add_action( 'template_redireect', array( $plugin, 'enforce_privacy_redirect' ), 0 );
+ add_filter( 'the_content_feed', array( $plugin, 'enforce_privacy_feeds' ), 999 );
+ add_filter( 'the_excerpt_rss', array( $plugin, 'enforce_privacy_feeds' ), 999 );
+ add_filter( 'comment_text_rss', array( $plugin, 'enforce_privacy_feeds' ), 999 );
}
}