Subversion Repositories php_users

Rev

Rev 6 | Rev 11 | Go to most recent revision | Show entire file | Ignore whitespace | Details | Blame | Last modification | View Log | RSS feed

Rev 6 Rev 10
Line -... Line 1...
-
 
1
====== PHP Users Class ======
-
 
2
 
-
 
3
I got frustrated trying to find a class or library to authenticate user logins in PHP. The ones I found were either too simplistic, or required me to "join" some project just to have access to purportedly open source software.
-
 
4
 
-
 
5
So, I decided to dust off the neurons and see if I could build one. I decided to make it as flexible as possible, with only the very basics, but able to be enhanced via data calls. I also decided to make the data access independent of the class itself so data access classes could be (re)written for tasks other than MySQL using the mysqli library.
-
 
6
 
1
By itself, the users class (with a data access class usersDataSource like the included UsersDataSourceMySQLi class) handles basic login/logout/editing functions.
7
By itself, the users class (with a data access class usersDataSource like the included UsersDataSourceMySQLi class) handles basic login/logout/editing functions.
2
 
8
 
3
The class(es) were, however, designed for extensibility and customization in mind. Some design considerations were made with this in mind.
9
The class(es) were, however, designed for extensibility and customization in mind. Some design considerations were made with this in mind.
4
 
10
 
5
This code uses the ternary and null coelescing shortcuts ?: and ??. I THINK these were introduced in PHP 5.3, but not sure. This code will not work on versions which do not have these shortcuts. See https://www.php.net/manual/en/language.operators.comparison.php
11
NOTE: This code uses the ternary and null coelescing shortcuts ?: and ??. I THINK these were introduced in PHP 5.3, but not sure. This code will not work on versions which do not have these shortcuts. See https://www.php.net/manual/en/language.operators.comparison.php
6
 
12
 
7
You can get a copy of this from our subversion repository
13
You can get a copy of this from our subversion repository
-
 
14
<code bash>
8
svn co http://svn.dailydata.net/svn/php_users/stable php_users
15
svn co http://svn.dailydata.net/svn/php_users/stable php_users
-
 
16
</code>
9
My working copy is at
17
My working copy is at
10
http://svn.dailydata.net/svn/php_users/trunk
18
http://svn.dailydata.net/svn/php_users/trunk
11
but I recommend NOT using that as I use trunk as my personal playground and will commit broken code to it regularly
19
but I recommend NOT using that as I use trunk as my personal playground and will commit broken code to it regularly
12
 
20
 
13
=== Basic System ===
21
==== Basic System ====
14
 
22
 
15
With no modification, the system will store username, password and two booleans, isAdmin and enabled. The default table is created as
23
With no modification, the system will store username, password and two booleans, isAdmin and enabled. The default table is created as
16
 
24
 
17
<code sql>
25
<code sql>
18
create or replace table _users (
26
create or replace table _users (
19
   _user_id    int unsigned not null auto_increment,
27
   _user_id    int unsigned not null auto_increment,
20
   login       varchar(64),
28
   login       varchar(64),
21
   password    varchar(128),
29
   password    varchar(128),
22
   isAdmin     boolean,
30
   isAdmin     boolean DEFAULT 1,
23
   enabled     boolean,
31
   enabled     boolean DEFAULT 1,
24
   primary key (_user_id)
32
   primary key (_user_id)
25
);
33
);
26
</code>
34
</code>
27
 
35
 
28
* login is filtered to alpha-numerics and the underscore character
36
  * login is filtered to alpha-numerics and the underscore character
29
* password is stored as a hash using PHP's password_hash
37
  * password is stored as a hash using PHP's password_hash
30
* users with isAdmin set will be able to add/edit other users
38
  * users with isAdmin set will be able to add/edit other users
31
* users with enabled set to false (0) will not be able to log in
39
  * users with enabled set to false (0) will not be able to log in
32
 
40
 
33
NOTE: the usersDataSource class has a public function, buildTable, which will build the table, so installation involves simply calling that function.
41
NOTE: the usersDataSource class has a public function, buildTable, which will build the table, so installation involves simply calling that function.
34
 
42
 
35
IMPORTANT: to allow the Users class to work with a wide variety of data types, it does no data access itself. It requires a data access class.
43
IMPORTANT: to allow the Users class to work with a wide variety of data types, it does no data access itself. It requires a data access class.
36
 
44
 
Line 70... Line 78...
70
 
78
 
71
If you run it the first time with <code php>$connection->buildTable( 'admin', 'admin' ); die;</code> uncommented, it will build the table. Comment that line out on the next run and you will be presented with a login screen.
79
If you run it the first time with <code php>$connection->buildTable( 'admin', 'admin' ); die;</code> uncommented, it will build the table. Comment that line out on the next run and you will be presented with a login screen.
72
 
80
 
73
Class function HTML() displays various things to allow login, then quits displaying anything. Setting $_REQUEST['logout'] = 1 before calling HTML() will initiate a log out which will destroy the session variable
81
Class function HTML() displays various things to allow login, then quits displaying anything. Setting $_REQUEST['logout'] = 1 before calling HTML() will initiate a log out which will destroy the session variable
74
 
82
 
75
=== Full Functionality ===
83
==== Full Functionality ====
76
 
84
 
77
Calling the class method admin and displaying the repeatedly will generate output allowing the user to change their username, password and any other fields which do not have the 'restrict' attribute set to true
85
Calling the class method admin and displaying the repeatedly will generate output allowing the user to change their username, password and any other fields which do not have the 'restrict' attribute set to true
78
 
86
 
79
If the user has admin rights set, it will also display a list of logins and allow you to select one to edit or, add a new user. Editing someone else shows all fields, whether or not the 'restrict' attribute is set to true.
87
If the user has admin rights set, it will also display a list of logins and allow you to select one to edit or, add a new user. Editing someone else shows all fields, whether or not the 'restrict' attribute is set to true.
80
 
88
 
81
=== CSS ===
89
==== CSS ====
82
 
90
 
83
I tried to not put any HTML layout into the code, relying instead on CSS. Everything is supposed to have a class and be wrapped in a <div> with a class. Following are the classes I have in the code (I THINK).
91
I tried to not put any HTML layout into the code, relying instead on CSS (Thanks, Randell). Everything is supposed to have a class and be wrapped in a <div> with a class. Following are the classes I have in the code (I THINK).
84
* login_field = This is the class of all INPUT fields, and div's surrounding them
92
* login_field = This is the class of all INPUT fields, and div's surrounding them
85
* login_form  = the class for all <form definitions
93
* login_form  = the class for all <form definitions
86
* login_list  = the class of the unsigned list (ul) which displays the links to edit other users for admin's
94
* login_list  = the class of the unsigned list (ul) which displays the links to edit other users for admin's
87
* login       = NOT in code, but used in example as a div around the div around the login area
95
* login       = NOT in code, but used in example as a div around the div around the login area
88
 
96
 
89
=== Extended Functionality ===
97
==== Extended Functionality ====
90
 
98
 
91
First, everything is pretty basic. I tried to limit the number of fields to the absolute minimum, but also set it up to allow additional fields to be added programmatically. The example does this.
99
First, everything is pretty basic. I tried to limit the number of fields to the absolute minimum, but also set it up to allow additional fields to be added programmatically. The example does this.
92
 
100
 
93
If you open the class source, you'll find the private member $dbDefinition, which defines everything in the code (I hope). When you create a new instance, you can pass the constructor an array which will be merged with this member, optionally increasing the number and definition of the fields.
101
If you open the class source, you'll find the private member $dbDefinition, which defines everything in the code (I hope). When you create a new instance, you can pass the constructor an array which will be merged with this member, optionally increasing the number and definition of the fields.
94
 
102
 
Line 140... Line 148...
140
         array( 'username' => 'test', 'password' => 'test', 'database' => 'test' ) 
148
         array( 'username' => 'test', 'password' => 'test', 'database' => 'test' ) 
141
      );
149
      );
142
   if ( ! isset( $_SESSION['user'] ) ) { 
150
   if ( ! isset( $_SESSION['user'] ) ) { 
143
      $_SESSION['user'] = new Users( $customFields );
151
      $_SESSION['user'] = new Users( $customFields );
144
   }
152
   }
145
</php>
153
</code>
146
 
154
 
147
Note that since we replicated the basic structure of $dbDefinition in Users and usersDataSource, we can use the same hash to pass into both; they will store, but ignore values not relevant to them.
155
Note that since we replicated the basic structure of $dbDefinition in Users and usersDataSource, we can use the same hash to pass into both; they will store, but ignore values not relevant to them.
148
 
156
 
149
When the usersDataSource and Users objects are created, $customFields will be merged, with duplicates overwritten by the $customFields value.
157
When the usersDataSource and Users objects are created, $customFields will be merged, with duplicates overwritten by the $customFields value.
150
 
158
 
151
This is not limited to adding new columns; you can modify the display definitions also, ie how the information is stored on the screen, to some extent.
159
This is not limited to adding new columns; you can modify the display definitions also, ie how the information is stored on the screen, to some extent.
152
 
160
 
-
 
161
=== New Column Definitions ===
-
 
162
 
-
 
163
Note: If a new column is defined with the name (see below) of 'last password change', anytime a password is changed, it will be updated to the current date/time in YYYYMMDDHHMMSS format, which can be directly plugged into MySQL. The code is in there, but to not overpopulate the tables with columns that may not be necessary, I did not include this column in the default table structure.
-
 
164
 
-
 
165
The structure of a new column only requires a value for 
-
 
166
  * type
-
 
167
  * html type
-
 
168
  * dbColumn
-
 
169
Defaults (generally empty strings) will be used for anything else.
-
 
170
 
-
 
171
== key name ==
-
 
172
The key is used throughout the program to identify what column we are working on. It can be any value that can be used as a PHP hash key.
-
 
173
 
-
 
174
== html type (required) ==
-
 
175
This determines how the field is displayed and processed during editing. Accepted values are:
-
 
176
  * text - standard text input field, corresponds to SQL varchar or char field
-
 
177
  * textarea - multi-line strings of arbitrary lenght, corresponds to mysql text field
-
 
178
  * boolean - displayed as checkbox, corresponds to mysql bool and/or tinyint (ie, 0 and 1 only)
-
 
179
  * password - displayed as a type='password' field, not pre-populated or displayed when typing. Uses varchar(128) in mysql for the length of the hash created
-
 
180
Any value not in the list above will probably result in weird errors.
-
 
181
 
-
 
182
== dbColumn (required) ==
-
 
183
This is the column name in the database for this field, and all sql uses this value when accessing a column.
-
 
184
 
-
 
185
== type (required for creating tables) ==
-
 
186
this is a valid MySQL database type used by the buildTable function to create the table, ie varchar, char, 
-
 
187
 
-
 
188
== label ==
-
 
189
If set, will be the label for the input screens. If not defined, the label defaults to the key.
-
 
190
 
-
 
191
== instructions ==
-
 
192
Replaces the html TITLE attribute for an INPUT, which is displayed by default on a hover. If not set, defaults to an empty string.
-
 
193
 
-
 
194
== hint ==
-
 
195
Placed in the PLACEHODER attribute for an INPUT. Displayed in an empty text field most of the time. If not set, defaults to an empty string.
-
 
196
 
-
 
197
== restrict ==
-
 
198
If set to true, will not be displayed when a user is editing their own record (so, not updateable by a user). Examples would be admin and enabled, which would not be something a user should change themselves. If an admin is editing a different user, these fields are available.
-
 
199
 
-
 
200
== filter ==
-
 
201
If set, this is assumed to be a regular express. The result of the input is checked against the regex. If it does not match the regex, the update is declined and an error message displayed. By default, the username can only be alpha-numeric and an underscore, so the regex '/[a-zA-Z0-9_]/' is set as the filter. If a user puts a period in their password, it will be rejected. Validity of the regex is not checked.
-
 
202
 
-
 
203
== size ==
-
 
204
For database creation, if set, will create a column (like varchar) with this size, ie size='128' in a varchar field will result in varchar(128)
-
 
205
 
-
 
206
== required ==
-
 
207
For database creation, sets the NOT NULL attribute to the column
-
 
208
 
-
 
209
== default ==
-
 
210
For database creation, sets the DEFAULT attribute for the column
-
 
211
 
153
==== usersDataSource ====
212
==== usersDataSource ====
154
 
213
 
155
This is our data access class. It really doesn't matter what it is called, though I plan to call it the same when I add more data access objects.
214
This is our data access class. It really doesn't matter what it is called, though I plan to call it the same when I add more data access objects.
156
 
215
 
157
This code accesses the data (duh), and is consistently called $connection in the Users class. The only requirement is that it must be able to implement the following functions
216
This code accesses the data (duh), and is consistently called $connection in the Users class. The only requirement is that it must be able to implement the following functions
Line 165... Line 224...
165
It is also nice is it can A) handle new columns and B) create and initialize the necessary storage
224
It is also nice is it can A) handle new columns and B) create and initialize the necessary storage
166
 
225
 
167
I separated this out from the Users class because not all programs need database access. For instance, the favorites_urls app uses file based storage, so by writing a new access class for it, we will hopefully be able to get the same functionality, but with a different storage back end.
226
I separated this out from the Users class because not all programs need database access. For instance, the favorites_urls app uses file based storage, so by writing a new access class for it, we will hopefully be able to get the same functionality, but with a different storage back end.
168
 
227
 
169
==== Future ====
228
==== Future ====
170
 
-
 
-
 
229
  * I think I have a way of making it match things like INPUT TYPE='color' and INPUT TYPE='email'. I'm going to check that out.
171
This is only the initial part of this particular project. I now intend to extend both classes to allow boolean permissions which will be integrated into our new version of CAMP, giving very granular rights to users. It will be available as a second set of files in this repository and is planned for release by October 2021.
230
  * This is only the initial part of this particular project. I now intend to extend both classes to allow boolean permissions which will be integrated into our new version of CAMP, giving very granular rights to users. It will be available as a second set of files in this repository and is planned for release by October 2021.