├── login-warning.php
├── readme.txt
└── wp-bouncer.php
/login-warning.php:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | Login Warning
6 |
7 |
13 |
14 |
15 |
16 |
17 |
Login Warning
18 |
There was an issue with your log in. Your user account has logged in recently from a different location.
19 |
20 |
21 |
22 |
--------------------------------------------------------------------------------
/readme.txt:
--------------------------------------------------------------------------------
1 | === WP Bouncer ===
2 | Contributors: norcross
3 | Website Link: http://andrewnorcross.com/plugins/wp-bouncer/
4 | Donate link: https://andrewnorcross.com/donate
5 | Tags: login, security, member, members, membership, memberships, susbcription, subscriptions
6 | Requires at least: 3.0
7 | Tested up to: 3.5
8 | Stable tag: 1.0.1
9 | License: GPLv2 or later
10 | License URI: http://www.gnu.org/licenses/gpl-2.0.html
11 |
12 | Only allow one device to be logged into WordPress for each user.
13 |
14 | == Description ==
15 |
16 | WP Bouncer will make sure users are logged in from only one device at a time. This should deter people from sharing their login credentials for your site, which is especially good for paid membership sites.
17 |
18 | WP Bouncer works by:
19 | * Storing a random "FAKESESSID" for each user when they log in.
20 | * If a user is logged in, on each page load (init hook), WP Bouncer checks if the FAKESESSID stored in the user's cookies is the same as the last login stored in a transient (fakesessid_user_login).
21 | * If not, WP Bouncer logs the user out and redirects them to a warning message.
22 |
23 | For Example:
24 | * User A logs in as "user". Their FAKESESSID, say "SESSION_A" is stored in a WordPress option.
25 | * User B logs in as "user". Their FAKESESSID, say "SESSION_B" is overwrites the stored WordPress option.
26 | * User A tries to load a page on your site, WP Bouncer catches them and logs them out, redirecting them to the warning message.
27 | * User B can browse around the site as normal... unless...
28 | * User A logs in again as "user". Their FAKESESSID, SESSION_A_v2 is stored in the WordPress option.
29 | * Now user B would be logged out if they load another page.
30 |
31 | Improvements:
32 | * Settings page to choose where users are taken after being bounced.
33 | * Keep track of how many bounces there are and lock the account down if there are so many in a small time frame.
34 |
35 | == Installation ==
36 |
37 | This section describes how to install the plugin and get it working.
38 |
39 | 1. Upload `wp-bouncer` to the `/wp-content/plugins/` directory.
40 | 2. Activate the plugin through the 'Plugins' menu in WordPress.
41 | 3. That's it! There are no settings for this plugin.
42 |
43 | == Frequently Asked Questions ==
44 |
45 | = What's this all about? =
46 |
47 | Something
48 |
49 | == Screenshots ==
50 |
51 | 1. Something
52 |
53 |
54 | == Changelog ==
55 |
56 | = 1.0.1 =
57 | * Fixed bug with how transients were being set and get.
58 | * Removed code in track_login that made sure you were logging in from login page. This will allow wp bouncer to kick in when logging in via wp_signon, etc.
59 | * Moved redirect url to a class property. Will eventually add a settings page for this and any other setting/configuration value.
60 |
61 | = 1.0 =
62 | * First release!
63 |
64 |
65 | == Upgrade Notice ==
66 |
67 | = 1.0 =
68 | * First release!
69 |
--------------------------------------------------------------------------------
/wp-bouncer.php:
--------------------------------------------------------------------------------
1 | redirect = esc_url_raw( plugin_dir_url( __FILE__ ) . 'login-warning.php' );
47 | }
48 |
49 | /**
50 | * helper function to get browser data at login
51 | *
52 | * @return WP_Bouncer
53 | */
54 |
55 | private function browser_data() {
56 |
57 | // grab base user agent and parse out
58 | $u_agent = $_SERVER['HTTP_USER_AGENT'];
59 | $bname = 'Unknown';
60 | $platform = 'Unknown';
61 | $version = '';
62 |
63 | // determine platform
64 | if (preg_match('/linux/i', $u_agent))
65 | $platform = 'linux';
66 |
67 | if (preg_match('/macintosh|mac os x/i', $u_agent))
68 | $platform = 'mac';
69 |
70 | if (preg_match('/windows|win32/i', $u_agent))
71 | $platform = 'windows';
72 |
73 |
74 | // get browser info
75 | if(preg_match('/MSIE/i',$u_agent) && !preg_match('/Opera/i',$u_agent)) {
76 | $bname = 'Internet Explorer';
77 | $ub = 'MSIE';
78 | }
79 |
80 | if(preg_match('/Firefox/i',$u_agent)) {
81 | $bname = 'Mozilla Firefox';
82 | $ub = 'Firefox';
83 | }
84 |
85 | if(preg_match('/Chrome/i',$u_agent)) {
86 | $bname = 'Google Chrome';
87 | $ub = 'Chrome';
88 | }
89 |
90 | if(preg_match('/Safari/i',$u_agent) && !preg_match('/Chrome/i',$u_agent)) {
91 | $bname = 'Apple Safari';
92 | $ub = 'Safari';
93 | }
94 |
95 | if(preg_match('/Opera/i',$u_agent)) {
96 | $bname = 'Opera';
97 | $ub = 'Opera';
98 | }
99 |
100 | if(preg_match('/Netscape/i',$u_agent)) {
101 | $bname = 'Netscape';
102 | $ub = 'Netscape';
103 | }
104 |
105 | // finally get the correct version number
106 | $known = array('Version', $ub, 'other');
107 | $pattern = '#(?' . join('|', $known) . ')[/ ]+(?[0-9.|a-zA-Z.]*)#';
108 |
109 | if (!preg_match_all($pattern, $u_agent, $matches)) {
110 | // we have no matching number just continue
111 | }
112 |
113 | // see how many we have
114 | $i = count( $matches['browser'] );
115 | if ($i != 1) {
116 | //we will have two since we are not using 'other' argument yet
117 | //see if version is before or after the name
118 | if (strripos( $u_agent, 'Version' ) < strripos($u_agent,$ub)){
119 | $version= $matches['version'][0];
120 | }
121 | else {
122 | $version= $matches['version'][1];
123 | }
124 | }
125 | else {
126 | $version= $matches['version'][0];
127 | }
128 |
129 | // check if we have a number
130 | if ($version == null || $version == '' )
131 | $version = '?';
132 |
133 | return array(
134 | 'userAgent' => $u_agent,
135 | 'name' => $bname,
136 | 'version' => $version,
137 | 'platform' => $platform,
138 | 'pattern' => $pattern
139 | );
140 | }
141 |
142 | /**
143 | * redirect function for flagged logins
144 | *
145 | * @return WP_Bouncer
146 | */
147 |
148 | public function flag_redirect() {
149 |
150 | $base = plugin_dir_url( __FILE__ );
151 | wp_redirect( $this->redirect );
152 | exit();
153 |
154 | }
155 |
156 | /**
157 | * run checks for a flagged login
158 | *
159 | * @return WP_Bouncer
160 | */
161 |
162 | public function login_flag() {
163 |
164 | if(is_user_logged_in())
165 | {
166 | global $current_user;
167 |
168 | //check the fakesessid
169 | $fakesessid = get_transient("fakesessid_" . $current_user->user_login);
170 |
171 | //krumo("fakesessid_" . $current_user->user_login);
172 | //krumo($fakesessid);
173 |
174 | if(!empty($fakesessid))
175 | {
176 | if(empty($_COOKIE['fakesessid']) || $fakesessid != $_COOKIE['fakesessid'])
177 | {
178 | //log user out
179 | wp_logout();
180 |
181 | //redirect
182 | $this->flag_redirect();
183 | }
184 | }
185 | }
186 | }
187 |
188 | /**
189 | * track and set session data at login
190 | *
191 | * @return WP_Bouncer
192 | */
193 |
194 | public function login_track($user_login) {
195 | // get browser data from current login
196 | $browser = $this->browser_data();
197 |
198 | //store a "fake" session id in transient and cookie
199 | $fakesessid = md5($browser['name'] . $browser['platform'] . $_SERVER['REMOTE_ADDR'] . time());
200 | set_transient("fakesessid_" . $user_login, $fakesessid, 3600*24*30);
201 | setcookie("fakesessid", $fakesessid, time()+3600*24*30, COOKIEPATH, COOKIE_DOMAIN, false);
202 | }
203 |
204 | /**
205 | * load textdomain
206 | *
207 | * @return WP_Bouncer
208 | */
209 |
210 |
211 | public function textdomain() {
212 |
213 | load_plugin_textdomain( 'wp-bouncer', false, dirname( plugin_basename( __FILE__ ) ) . '/languages/' );
214 | }
215 |
216 |
217 | /// end class
218 | }
219 |
220 |
221 | // Instantiate our class
222 | $WP_Bouncer = new WP_Bouncer();
223 |
--------------------------------------------------------------------------------