PluginProbe
DoLogin Security / trunk
DoLogin Security vtrunk
5.0.10 4.8.3 trunk 1.0 1.1 1.1.1 1.2 1.2.1 1.2.2 1.3 1.3.1 1.3.2 1.3.3 1.3.4 1.3.5 1.4 1.4.1 1.4.2 1.4.3 1.4.4 1.4.5 1.4.6 1.4.7 1.5 1.6 All 64 releases
dologin / src / admin.cls.php

admin.cls.php in DoLogin Security trunk, at src/admin.cls.php

268 lines 8.3 KB
No matching file
Up and down to move Enter to open Esc to close
Raw Download Zip
1 <?php
2 /**
3 * Admin class
4 *
5 * @since 1.0
6 */
7 namespace dologin;
8
9 defined( 'WPINC' ) || exit;
10
11 class Admin extends Instance {
12 /**
13 * Init admin
14 *
15 * @since 1.0
16 * @access public
17 */
18 public function init() {
19 add_action( 'admin_menu', array( $this, 'admin_menu' ) );
20 add_filter( 'plugin_action_links_dologin/dologin.php', array( $this, 'add_plugin_links' ) );
21 add_action( 'admin_init', array( $this, 'admin_init' ) );
22
23 add_action( 'admin_enqueue_scripts', array( $this->cls( 'GUI' ), 'enqueue_admin' ) );
24
25 add_action( 'wp_dashboard_setup', array( $this, 'dashboard_widget' ) );
26 }
27
28 /**
29 * Register a dashboard widget
30 */
31 public function dashboard_widget() {
32 wp_add_dashboard_widget( 'dologin', __( 'DoLogin Security Overview', 'dologin' ), array( $this, 'widget_overview' ) );
33 }
34
35 /**
36 * Overview widget
37 */
38 public function widget_overview() {
39 require_once DOLOGIN_DIR . 'tpl/widget.tpl.php';
40 }
41
42 /**
43 * Admin setting page
44 *
45 * @since 1.0
46 * @access public
47 */
48 public function admin_menu() {
49 add_options_page( 'DoLogin Security', 'DoLogin Security', apply_filters( 'dologin_admin_menu_access', 'manage_options' ), 'dologin', array( $this, 'setting_page' ) );
50
51 $this->cls( 'TwoFA' )->maybe_save_2fa();
52 }
53
54 /**
55 * admin_init
56 *
57 * @since 1.2.2
58 * @access public
59 */
60 public function admin_init() {
61 if ( get_transient( 'dologin_activation_redirect' ) ) {
62 delete_transient( 'dologin_activation_redirect' );
63 if ( ! is_network_admin() && ! isset( $_GET['activate-multi'] ) ) { // phpcs:ignore WordPress.Security.NonceVerification.Recommended -- reading WordPress core activation flag, no state change.
64 wp_safe_redirect( menu_page_url( 'dologin', 0 ) );
65 }
66 }
67
68 // Hide authentication fields that are managed only by dedicated flows.
69 add_filter( 'user_contactmethods', array( $this, 'user_contactmethods' ), 10, 1 );
70 add_filter( 'manage_users_columns', array( $this, 'manage_users_columns' ) );
71 add_filter( 'manage_users_custom_column', array( $this, 'manage_users_custom_column' ), 10, 3 );
72
73 add_action( 'admin_notices', array( $this->cls( 'GUI' ), 'display_msg' ) );
74 }
75
76 /**
77 * Remove authentication fields that cannot be edited on the standard profile page.
78 *
79 * @since 1.3
80 */
81 public function user_contactmethods( $contactmethods ) {
82 // The 2FA secret is managed only by its enrollment flow and must not appear in standard profile fields.
83 unset( $contactmethods['2fa'] );
84 return $contactmethods;
85 }
86
87 public function manage_users_columns( $column ) {
88 if ( ! array_key_exists( 'dologin_operations', $column ) ) {
89 $column['dologin_operations'] = __( 'Dologin Operations', 'dologin' );
90 }
91 if ( ! array_key_exists( '2fa', $column ) ) {
92 $column['2fa'] = __( 'Dologin 2FA', 'dologin' );
93 }
94 return $column;
95 }
96
97 public function manage_users_custom_column( $val, $column_name, $user_id ) {
98 if ( 'dologin_operations' === $column_name ) {
99 $val = '<div class="dologin"><a href="' . esc_url( Util::build_url( Router::ACTION_SITE, Site::TYPE_GEN_TOKEN, false, null, array( 'uid' => $user_id ) ) ) . '" class="button dologin-btn-tiny dologin-btn-success dologin-mb10">' . esc_html__( 'Create Site Token', 'dologin' ) . '</a>';
100 $val .= ' <a href="' . esc_url( Util::build_url( Router::ACTION_PSWD, Pswdless::TYPE_GEN, false, null, array( 'uid' => $user_id ) ) ) . '" class="button dologin-btn-primary dologin-btn-tiny">' . esc_html__( 'Generate Login Link', 'dologin' ) . '</a></div>';
101
102 return $val;
103 }
104
105 if ( $column_name == '2fa' ) {
106 $val = get_the_author_meta( '2fa', $user_id );
107 $val = $val ? 'Enabled' : '-';
108 }
109
110 return $val;
111 }
112
113 /**
114 * Plugin link
115 *
116 * @since 1.1
117 * @access public
118 */
119 public function add_plugin_links( $links ) {
120 $links[] = '<a href="' . menu_page_url( 'dologin', 0 ) . '">' . __( 'Settings', 'dologin' ) . '</a>';
121
122 return $links;
123 }
124
125 /**
126 * Display and save options
127 *
128 * @since 1.0
129 * @access public
130 */
131 public function setting_page() {
132 $this->cls( 'Data' )->tables_create();
133
134 if ( ! empty( $_POST ) ) {
135 check_admin_referer( 'dologin' );
136
137 $raw_data = self::cleanup_text( $_POST );
138 $force_was_enabled = (bool) Conf::val( 'kl_sso_force' );
139
140 // Save options
141 $list = array();
142
143 foreach ( $this->cls( 'Conf' )->get_options() as $id => $v ) {
144 if ( substr( $id, 0, 1 ) === '_' ) {
145 continue;
146 }
147
148 $list[ $id ] = ! empty( $raw_data[ $id ] ) ? $raw_data[ $id ] : false;
149 }
150
151 // Special handler for list
152 $list['whitelist'] = $this->_sanitize_list( $raw_data['whitelist'] );
153 $list['blacklist'] = $this->_sanitize_list( $raw_data['blacklist'] );
154
155 $save_errors = array();
156 if ( ! empty( $list['kl_sso'] ) ) {
157 $requirements = KLSso::requirements();
158 if ( empty( $list['kl_sso_svc_id'] ) ) {
159 $requirements[] = __( 'KeyLockr App Tag is required.', 'dologin' );
160 }
161 if ( ! empty( $requirements ) ) {
162 $list['kl_sso'] = false;
163 $save_errors[] = __( 'KeyLockr SSO was not enabled.', 'dologin' ) . ' ' . implode( ' ', $requirements );
164 }
165 }
166 if ( ! $force_was_enabled && ! empty( $list['kl_sso_force'] ) && ( empty( $list['kl_sso'] ) || ! KLSso::force_ready( $list['kl_sso_svc_id'] ) ) ) {
167 $list['kl_sso_force'] = false;
168 $save_errors[] = __( 'Force KeyLockr SSO was not enabled. Link and verify the current administrator with the saved App Tag and site keys first.', 'dologin' );
169 }
170
171 foreach ( $list as $id => $v ) {
172 Conf::update( $id, $v );
173 }
174
175 if ( $save_errors ) {
176 GUI::error( implode( '<br />', array_map( 'esc_html', $save_errors ) ), true );
177 } else {
178 GUI::succeed( __( 'Options saved successfully!', 'dologin' ), true );
179 }
180
181 wp_safe_redirect( menu_page_url( 'dologin', false ) );
182 exit;
183 }
184
185 require_once DOLOGIN_DIR . 'tpl/entry.tpl.php';
186 }
187
188 /**
189 * Clean up the input string of any extra slashes/spaces.
190 *
191 * @access public
192 */
193 public static function cleanup_text( $input ) {
194 if ( is_array( $input ) ) {
195 return array_map( __CLASS__ . '::cleanup_text', $input );
196 }
197
198 return stripslashes( trim( $input ) );
199 }
200
201 /**
202 * Sanitize list
203 *
204 * @since 1.0
205 * @access public
206 */
207 private function _sanitize_list( $list ) {
208 if ( ! is_array( $list ) ) {
209 $list = explode( "\n", trim( $list ) );
210 }
211
212 foreach ( $list as $k => $v ) {
213 $list[ $k ] = implode( ', ', array_map( 'trim', explode( ',', $v ) ) );
214 }
215
216 return array_filter( $list );
217 }
218
219 /**
220 * Display pswdless
221 *
222 * @since 1.4
223 * @access public
224 */
225 public function pswdless_log() {
226 global $wpdb;
227
228 $list = $wpdb->get_results( 'SELECT * FROM ' . $this->cls( 'Data' )->tb( 'pswdless' ) . ' ORDER BY id DESC' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery -- table name is a hardcoded internal identifier.
229 foreach ( $list as $k => $v ) {
230 $user_info = get_userdata( $v->user_id );
231 $list[ $k ]->username = $user_info ? $user_info->user_login : __( 'Deleted user', 'dologin' );
232 }
233
234 return $list;
235 }
236
237 /**
238 * Display child sites
239 *
240 * @since 4.0
241 * @access public
242 */
243 public function sites() {
244 global $wpdb;
245
246 $list = $wpdb->get_results( 'SELECT * FROM ' . $this->cls( 'Data' )->tb( 'site' ) . ' ORDER BY id DESC' ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared,WordPress.DB.PreparedSQL.InterpolatedNotPrepared, PluginCheck.Security.DirectDB.UnescapedDBParameter, WordPress.DB.DirectDatabaseQuery -- table name is a hardcoded internal identifier.
247 foreach ( $list as $k => $v ) {
248 $user_info = get_userdata( $v->user_id );
249 $list[ $k ]->username = $user_info ? $user_info->user_login : __( 'Deleted user', 'dologin' );
250 $roles = array();
251 $easy_login = '';
252 if ( $v->is_child && $user_info ) {
253 $roles = $user_info->roles;
254 } else {
255 $easy_login = Util::build_url( Router::ACTION_SITE, Site::TYPE_EASY_LOGIN, false, null, array( 'dologin_id' => $v->id ) );
256 }
257 $list[ $k ]->roles = $roles;
258 $list[ $k ]->easy_login = $easy_login;
259 $list[ $k ]->_lock_link = Util::build_url( Router::ACTION_SITE, Pswdless::TYPE_LOCK, false, null, array( 'dologin_id' => $v->id ) );
260 $list[ $k ]->_del_link = Util::build_url( Router::ACTION_SITE, Pswdless::TYPE_DEL, false, null, array( 'dologin_id' => $v->id ) );
261 $list[ $k ]->_valid = time() - $v->dateline <= 3600;
262 }
263
264 return $list;
265 }
266
267 }
268