summaryrefslogtreecommitdiff
path: root/daggerhart-openid-connect-generic/includes/openid-connect-generic-settings-page.php
blob: 5178521282c23c24029be86dede65597cd9d2bc5 (plain)
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
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
<?php

/**
 * Class OpenID_Connect_Generic_Settings_Page.
 * Admin settings page.
 */
class OpenID_Connect_Generic_Settings_Page {

	// local copy of the settings provided by the base plugin
	private $settings;

	// The controlled list of settings & associated
	// defined during construction for i18n reasons
	private $settings_fields = array();

	// options page slug
	private $options_page_name = 'openid-connect-generic-settings';

	// options page settings group name
	private $settings_field_group;

	/**
	 * @param OpenID_Connect_Generic_Option_Settings $settings
	 * @param OpenID_Connect_Generic_Option_Logger $logger
	 */
	function __construct( OpenID_Connect_Generic_Option_Settings $settings, OpenID_Connect_Generic_Option_Logger $logger ) {
		$this->settings             = $settings;
		$this->logger               = $logger;
		$this->settings_field_group = $this->settings->get_option_name() . '-group';

		/*
		 * Simple settings fields simply have:
		 *
		 * - title
		 * - description
		 * - type ( checkbox | text | select )
		 * - section - settings/option page section ( client_settings | authorization_settings )
		 * - example (optional example will appear beneath description and be wrapped in <code>)
		 */
		$fields = array(
			'login_type'        => array(
				'title'       => __( 'Login Type' ),
				'description' => __( 'Select how the client (login form) should provide login options.' ),
				'type'        => 'select',
				'options'     => array(
					'button' => __( 'OpenID Connect button on login form' ),
					'auto'   => __( 'Auto Login - SSO' ),
				),
				'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',
				'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.' ),
				'type'        => 'text',
				'section'     => 'client_settings',
			),
			'scope'             => array(
				'title'       => __( 'OpenID Scope' ),
				'description' => __( 'Space separated list of scopes this client should access.' ),
				'example'     => 'email profile openid offline_access',
				'type'        => 'text',
				'section'     => 'client_settings',
			),
			'endpoint_login'    => array(
				'title'       => __( 'Login Endpoint URL' ),
				'description' => __( 'Identify provider authorization endpoint.' ),
				'example'     => 'https://example.com/oauth2/authorize',
				'type'        => 'text',
				'section'     => 'client_settings',
			),
			'endpoint_userinfo' => array(
				'title'       => __( 'Userinfo Endpoint URL' ),
				'description' => __( 'Identify provider User information endpoint.' ),
				'example'     => 'https://example.com/oauth2/UserInfo',
				'type'        => 'text',
				'section'     => 'client_settings',
			),
			'endpoint_token'    => array(
				'title'       => __( 'Token Validation Endpoint URL' ),
				'description' => __( 'Identify provider token endpoint.' ),
				'example'     => 'https://example.com/oauth2/token',
				'type'        => 'text',
				'section'     => 'client_settings',
			),
			'endpoint_end_session'    => array(
				'title'       => __( 'End Session Endpoint URL' ),
				'description' => __( 'Identify provider logout endpoint.' ),
				'example'     => 'https://example.com/oauth2/logout',
				'type'        => 'text',
				'section'     => 'client_settings',
			),
			'identity_key'     => array(
				'title'       => __( 'Identity Key' ),
				'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',
			),
			'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.<br><strong>Not recommended for production sites.</strong>' ),
				'type'        => 'checkbox',
				'section'     => 'client_settings',
			),
			'http_request_timeout'      => array(
				'title'       => __( 'HTTP Request Timeout' ),
				'description' => __( 'Set the timeout for requests made to the IDP. Default value is 5.' ),
				'example'     => 30,
				'type'        => 'text',
				'section'     => 'client_settings',
			),
			'enforce_privacy'   => array(
				'title'       => __( 'Enforce Privacy' ),
				'description' => __( 'Require users be logged in to see the site.' ),
				'type'        => 'checkbox',
				'section'     => 'authorization_settings',
			),
			'alternate_redirect_uri'   => array(
				'title'       => __( 'Alternate Redirect URI' ),
				'description' => __( 'Provide an alternative redirect route. Useful if your server is causing issues with the default admin-ajax method. You must flush rewrite rules after changing this setting. This can be done by saving the Permalinks settings page.' ),
				'type'        => 'checkbox',
				'section'     => 'authorization_settings',
			),
			'nickname_key'     => array(
				'title'       => __( 'Nickname Key' ),
				'description' => __( 'Where in the user claim array to find the user\'s nickname. Possible standard values: preferred_username, name, or sub.' ),
				'example'     => 'preferred_username',
				'type'        => 'text',
				'section'     => 'client_settings',
			),
			'email_format'     => array(
				'title'       => __( 'Email Formatting' ),
				'description' => __( 'String from which the user\'s email address is built. Specify "{email}" as long as the user claim contains an email claim.' ),
				'example'     => '{email}',
				'type'        => 'text',
				'section'     => 'client_settings',
			),
			'displayname_format'     => array(
				'title'       => __( 'Display Name Formatting' ),
				'description' => __( 'String from which the user\'s display name is built.' ),
				'example'     => '{given_name} {family_name}',
				'type'        => 'text',
				'section'     => 'client_settings',
			),
			'identify_with_username'     => array(
				'title'       => __( 'Identify with User Name' ),
				'description' => __( 'If checked, the user\'s identity will be determined by the user name instead of the email address.' ),
				'type'        => 'checkbox',
				'section'     => 'client_settings',
			),
			'state_time_limit'     => array(
				'title'       => __( 'State time limit' ),
				'description' => __( 'State valid time in seconds. Defaults to 180' ),
				'type'        => 'number',
				'section'     => 'client_settings',
			),
			'link_existing_users'   => array(
				'title'       => __( 'Link Existing Users' ),
				'description' => __( 'If a WordPress account already exists with the same identity as a newly-authenticated user over OpenID Connect, login as that user instead of generating an error.' ),
				'type'        => 'checkbox',
				'section'     => 'user_settings',
			),
			'create_if_does_not_exist'   => array(
				'title'       => __( 'Create user if does not exist' ),
				'description' => __( 'If the user identity is not link to an existing Wordpress user, it is created. If this setting is not enabled and if the user authenticates with an account which is not link to an existing Wordpress user then the authentication failed' ),
				'type'        => 'checkbox',
				'section'     => 'user_settings',
			),
			'redirect_user_back'   => array(
				'title'       => __( 'Redirect Back to Origin Page' ),
				'description' => __( 'After a successful OpenID Connect authentication, this will redirect the user back to the page on which they clicked the OpenID Connect login button. This will cause the login process to proceed in a traditional WordPress fashion. For example, users logging in through the default wp-login.php page would end up on the WordPress Dashboard and users logging in through the WooCommerce "My Account" page would end up on their account page.' ),
				'type'        => 'checkbox',
				'section'     => 'user_settings',
			),
			'redirect_on_logout'   => array(
				'title'       => __( 'Redirect to the login screen session is expired' ),
				'description' => __( 'When enabled, this will automatically redirect the user back to the WordPress login page if their access token has expired.' ),
				'type'        => 'checkbox',
				'section'     => 'user_settings',
			),
			'enable_logging'    => array(
				'title'       => __( 'Enable Logging' ),
				'description' => __( 'Very simple log messages for debugging purposes.' ),
				'type'        => 'checkbox',
				'section'     => 'log_settings',
			),
			'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',
				'section'     => 'log_settings',
			),
		);

		$fields = apply_filters( 'openid-connect-generic-settings-fields', $fields );

		// some simple pre-processing
		foreach ( $fields as $key => &$field ) {
			$field['key']  = $key;
			$field['name'] = $this->settings->get_option_name() . '[' . $key . ']';
		}

		// allow alterations of the fields
		$this->settings_fields = $fields;
	}

	/**
	 * @param \OpenID_Connect_Generic_Option_Settings $settings
	 * @param \OpenID_Connect_Generic_Option_Logger $logger
	 *
	 * @return \OpenID_Connect_Generic_Settings_Page
	 */
	static public function register( OpenID_Connect_Generic_Option_Settings $settings, OpenID_Connect_Generic_Option_Logger $logger ){
		$settings_page = new self( $settings, $logger );

		// add our options page the the admin menu
		add_action( 'admin_menu', array( $settings_page, 'admin_menu' ) );

		// register our settings
		add_action( 'admin_init', array( $settings_page, 'admin_init' ) );

		return $settings_page;
	}

	/**
	 * Implements hook admin_menu to add our options/settings page to the
	 *  dashboard menu
	 */
	public function admin_menu() {
		add_options_page(
			__( 'OpenID Connect - Generic Client' ),
			__( 'OpenID Connect Client' ),
			'manage_options',
			$this->options_page_name,
			array( $this, 'settings_page' ) );
	}

	/**
	 * Implements hook admin_init to register our settings
	 */
	public function admin_init() {
		register_setting( $this->settings_field_group, $this->settings->get_option_name(), array(
			$this,
			'sanitize_settings'
		) );

		add_settings_section( 'client_settings',
			__( 'Client Settings' ),
			array( $this, 'client_settings_description' ),
			$this->options_page_name
		);

		add_settings_section( 'user_settings',
			__( 'WordPress User Settings' ),
			array( $this, 'user_settings_description' ),
			$this->options_page_name
		);

		add_settings_section( 'authorization_settings',
			__( 'Authorization Settings' ),
			array( $this, 'authorization_settings_description' ),
			$this->options_page_name
		);

		add_settings_section( 'log_settings',
			__( 'Log Settings' ),
			array( $this, 'log_settings_description' ),
			$this->options_page_name
		);

		// preprocess fields and add them to the page
		foreach ( $this->settings_fields as $key => $field ) {
			// make sure each key exists in the settings array
			if ( ! isset( $this->settings->{ $key } ) ) {
				$this->settings->{ $key } = null;
			}

			// determine appropriate output callback
			switch ( $field['type'] ) {
				case 'checkbox':
					$callback = 'do_checkbox';
					break;

				case 'select':
					$callback = 'do_select';
					break;

				case 'text':
				default:
					$callback = 'do_text_field';
					break;
			}

			// add the field
			add_settings_field( $key, $field['title'],
				array( $this, $callback ),
				$this->options_page_name,
				$field['section'],
				$field
			);
		}
	}

	/**
	 * Sanitization callback for settings/option page
	 *
	 * @param $input - submitted settings values
	 *
	 * @return array
	 */
	public function sanitize_settings( $input ) {
		$options = array();

		// loop through settings fields to control what we're saving
		foreach ( $this->settings_fields as $key => $field ) {
			if ( isset( $input[ $key ] ) ) {
				$options[ $key ] = sanitize_text_field( trim( $input[ $key ] ) );
			}
			else {
				$options[ $key ] = '';
			}
		}

		return $options;
	}

	/**
	 * Output the options/settings page
	 */
	public function settings_page() {
		$redirect_uri = admin_url( 'admin-ajax.php?action=openid-connect-authorize' );

		if ( $this->settings->alternate_redirect_uri ){
			$redirect_uri = site_url( '/openid-connect-authorize' );
		}
		?>
		<div class="wrap">
			<h2><?php print esc_html( get_admin_page_title() ); ?></h2>

			<form method="post" action="options.php">
				<?php
				settings_fields( $this->settings_field_group );
				do_settings_sections( $this->options_page_name );
				submit_button();

				// simple debug to view settings array
				if ( isset( $_GET['debug'] ) ) {
					var_dump( $this->settings->get_values() );
				}
				?>
			</form>

			<h4><?php _e( 'Notes' ); ?></h4>

			<p class="description">
				<strong><?php _e( 'Redirect URI' ); ?></strong>
				<code><?php print $redirect_uri; ?></code>
			</p>
			<p class="description">
				<strong><?php _e( 'Login Button Shortcode' ); ?></strong>
				<code>[openid_connect_generic_login_button]</code>
			</p>
			<p class="description">
				<strong><?php _e( 'Authentication URL Shortcode' ); ?></strong>
				<code>[openid_connect_generic_auth_url]</code>
			</p>

			<?php if ( $this->settings->enable_logging ) { ?>
				<h2><?php _e( 'Logs' ); ?></h2>
				<div id="logger-table-wrapper">
					<?php print $this->logger->get_logs_table(); ?>
				</div>

			<?php } ?>
		</div>
		<?php
	}

	/**
	 * Output a standard text field
	 *
	 * @param $field
	 */
	public function do_text_field( $field ) {
		?>
		<input type="<?php print esc_attr( $field['type'] ); ?>"
		       id="<?php print esc_attr( $field['key'] ); ?>"
		       class="large-text"
		       name="<?php print esc_attr( $field['name'] ); ?>"
		       value="<?php print esc_attr( $this->settings->{ $field['key'] } ); ?>">
		<?php
		$this->do_field_description( $field );
	}

	/**
	 * Output a checkbox for a boolean setting
	 *  - hidden field is default value so we don't have to check isset() on save
	 *
	 * @param $field
	 */
	public function do_checkbox( $field ) {
		?>
		<input type="hidden" name="<?php print esc_attr( $field['name'] ); ?>" value="0">
		<input type="checkbox"
		       id="<?php print esc_attr( $field['key'] ); ?>"
		       name="<?php print esc_attr( $field['name'] ); ?>"
		       value="1"
			<?php checked( $this->settings->{ $field['key'] }, 1 ); ?>>
		<?php
		$this->do_field_description( $field );
	}

	/**
	 * @param $field
	 */
	function do_select( $field ) {
		$current_value = isset( $this->settings->{ $field['key'] } ) ? $this->settings->{ $field['key'] } : '';
		?>
		<select name="<?php print esc_attr( $field['name'] ); ?>">
			<?php foreach ( $field['options'] as $value => $text ): ?>
				<option value="<?php print esc_attr( $value ); ?>" <?php selected( $value, $current_value ); ?>><?php print esc_html( $text ); ?></option>
			<?php endforeach; ?>
		</select>
		<?php
		$this->do_field_description( $field );
	}

	/**
	 * Simply output the field description, and example if present
	 *
	 * @param $field
	 */
	public function do_field_description( $field ) {
		?>
		<p class="description">
			<?php print $field['description']; ?>
			<?php if ( isset( $field['example'] ) ) : ?>
				<br/><strong><?php _e( 'Example' ); ?>: </strong>
				<code><?php print $field['example']; ?></code>
			<?php endif; ?>
		</p>
		<?php
	}

	public function client_settings_description() {
		_e( 'Enter your OpenID Connect identity provider settings' );
	}

	public function user_settings_description() {
		_e( 'Modify the interaction between OpenID Connect and WordPress users' );
	}

	public function authorization_settings_description() {
		_e( 'Control the authorization mechanics of the site' );
	}

	public function log_settings_description() {
		_e( 'Log information about login attempts through OpenID Connect Generic' );
	}
}