The Log-in Manager class is a static class that deals with the login of users to the system. More...
#include <DcLoginManager.h>
Classes | |
class | CdcDaaChallenge |
This is basically a struct to hold the challenges this class sends to users. | |
class | CdcDaaResponse |
This is a struct to hold the response we got from the user. | |
class | CdcUser |
CdcUser holds information of one user in the system. | |
Static Public Member Functions | |
static void | Load (const char *file_name) |
This function loads the users database to memory, and initialize some cryptographic features, and configuration variables. | |
static void | Save (const char *file_name) |
This function saves the users database to a file. | |
static CdcUserStatus * | CheckAccess (const CdcString &group, CdcRequest &req, CdcResponse &res, int &ret_code, CdcString &new_page) |
This is the main function of this class. | |
static bool | CheckLogout (CdcRequest &req, CdcResponse &res) |
This function should be called by CdcConnectionThread before starting to create any response page. | |
static bool | AddUser (const CdcString &username, const CdcString &password) |
Function to add a user to the class users database. | |
static bool | ChangePassword (const CdcString &username, const CdcString &old_password, const CdcString new_password) |
Function to change a user password. | |
static bool | RemoveUser (const CdcString &username, const CdcString &password) |
Function to remove users from the class database. | |
Friends | |
int | DcClean (void *pParam) |
Global function for the cleaning thread (runs in infinite loop). |
The Log-in Manager class is a static class that deals with the login of users to the system.
The logging process can be done in two ways. One is the RFC 2617 Digest Access Authentication (DAA) login process, and the other is a server built-in login system. In addition both ways can be combined.
A note should be made that the built-in login system is secure just if it is working with SSL (or some other) encryption of the communication between the server and the client. For full discussion of the login system see the Login System document.
The server has a list of protected folders or files, in the Login-Manager configuration part of the configuration files. Every file or folder that has been set to be protected, belongs to some group of users.
The groups list and their users are also listed in this section of the configuration files (as a tree). Each group is an entry in the configuration file, and under this entry you should write the users that belong to the group or other groups names.
The names of the login page, access-denied page and the name of the file of user-names and passwords, are also set in this configuration section.
The Digest Access Authentication (DAA): In every request (or include) of a protected file, the server will look for the "Authorization" header in the request. If it finds this header it will test it against the user password. If the server does not find this header it will send 401 response with a WWW-Authenticate header that includes all the needed data for the client to log in (the challenge). If the server did find the Authorization header, but the challenge for this response was too old (stale) it will add a "stale=true" to the challenge, so the user agent does not need to ask for the password again. See RFC 2617 for full details.
The built-in login process starts in a similar way. The server send the exact same challenge to the client, not in a header of 401 response, but embedded in a special login HTML web page. the user enter his user-name and password to an HTML form, then a (Javascript) calculation of the response should be made on the client side, and the the same response of a DAA process should be sent back as HTML request variables.
After the first log-in the server will maintain one-time random code for this user. This code will go back and forth between the server and the client (different code each time), and as long as the client knows the correct code, this user stays logged in. This process is what needs SSL for full security, as the random code is passed in the open. (With the random code the server sends the user an ID as well, so it can keep track of more then one user at one time.
Note that this login process require the use of some scripting language to embed the challenge parameters in the HTML page.
There is an option to combine the two methods, and use the DAA process in the beginning (so the user get the nice message box from the browser), but then use the one-time code method. This will save the need of a login page with complected Javascript, but will be more efficient than the full DAA process.
The way to tell the server which login method to use is by setting the name (and path) of the login page in the Login-Manager configuration section. if this file is missing the server will use DAA.
Some other configuration options are:
The Auto-Cookie parameter, if sets to "yes" will tell the server to insert the one-time code and user ID to a cookie response header, every time they exists, without the use of some scripting language. The Stale parameter will set the time in seconds for a challenge to be valid and the Expire parameter will set the time for a deletion of a challenge if it is not get any recent response. In addition there should be a name (and path) of a deny page, if a user is logged in but cannot see a certain page because he is not belong to the correct group. If an access to a page is denied, the class will change the page to the "Access Denied" page, You can still transfer the User Code and User ID in this page so the user will not be logged out.
This class starts a lower priority thread to keep the challenges list clean of old challenges. It signals this thread to scan the list after marking any challenge for deletion, and after logging out a user (see somewhat long explanation in the DcClean function).
Definition at line 137 of file DcLoginManager.h.
Function to add a user to the class users database.
username | The new username. |
password | The new password. |
(CdcException) | Out of memory exception. |
Definition at line 485 of file DcLoginManager.cpp.
bool CdcLoginManager::ChangePassword | ( | const CdcString & | username, |
const CdcString & | old_password, | ||
const CdcString | new_password | ||
) | [static] |
Function to change a user password.
username | The username of the user. |
old_password | The old password (for identification). |
new_password | The new password. |
Definition at line 501 of file DcLoginManager.cpp.
CdcUserStatus * CdcLoginManager::CheckAccess | ( | const CdcString & | group, |
CdcRequest & | req, | ||
CdcResponse & | res, | ||
int & | ret_code, | ||
CdcString & | new_page | ||
) | [static] |
This is the main function of this class.
it checks if the user is under a certain group, and knows the correct password. (see the description in the class description).
group | The group name this page is protected by. |
req | Reference to the request object. |
res | Reference to the response object. |
ret_code | [output] A return code that can be: DC_LM_OK if access is granted DC_LM_NONE if the group name was empty. DC_LM_SEND if need to send to user other (login/deny/DAA 401) page. |
new_page | If ret_code is DC_LM_SEND, new_page will get the relative path to the new page to send (or will be empty in DAA method), otherwise it remains untouched. |
Definition at line 346 of file DcLoginManager.cpp.
bool CdcLoginManager::CheckLogout | ( | CdcRequest & | req, |
CdcResponse & | res | ||
) | [static] |
This function should be called by CdcConnectionThread before starting to create any response page.
It checks if the user asked to be logged out, and if so logged him out.
req | Reference to the request object. |
res | Reference to the response object. |
Definition at line 170 of file DcLoginManager.cpp.
void CdcLoginManager::Load | ( | const char * | file_name ) | [static] |
This function loads the users database to memory, and initialize some cryptographic features, and configuration variables.
file_name | The name of the users database file. |
(CdcException) | If cannot open database file, or cannot allocate memory. |
Definition at line 79 of file DcLoginManager.cpp.
bool CdcLoginManager::RemoveUser | ( | const CdcString & | username, |
const CdcString & | password | ||
) | [static] |
Function to remove users from the class database.
username | The username of the user to remove. |
password | The password of the user to remove. |
Definition at line 516 of file DcLoginManager.cpp.
void CdcLoginManager::Save | ( | const char * | file_name ) | [static] |
This function saves the users database to a file.
file_name | The name of the users database file. |
(CdcException) | If cannot open database file. |
Definition at line 113 of file DcLoginManager.cpp.
int DcClean | ( | void * | pParam ) | [friend] |
Global function for the cleaning thread (runs in infinite loop).
The problem with the cleaning system is that the function CheckResponse can be called many times from many different threads, as if using DAA, this is the function that check access before sending any protected page. In addition the running time of this function is some what long as this is the function that do the response digest calculation on the server side.
If the cleaning thread will delete a taken challenge, the system will crush. On the other hand, I don't want to lock the Mutex for the entire running time of this function, because then it will mutilate other CheckResponse functions that needs to run in other threads. There is no reason way these functions cannot run in the same time.
To solve this problem I use "Taken Challenges" global counter. When CheckResponse is called it increases this counter by one, and waits on the Mutex. After getting the Mutex it releases it immediately and start working. When it is done, it decreases the counter by one. Every time a challenge needs to be deleted, a semaphore is signaled (just before CheckResponse returns).
The cleaning thread waits on this semaphore. After getting a signal, it sleeps for few seconds (from other reasons - the invalid challenge can still be used by the browser, if it is a multi-frame page). After the delay the thread calls the cleaning function. The cleaning function checks first if the counter value is zero, if not it exits immediately. If the counter is zero, it iterates through the challenges list and clean every challenge that is marked for deletion, and old unused challenges. in the beginning of every iteration cycle, it checks the counter again, if it is not zero, it exits immediately, so CheckResponse can start working. The uncleared challenges will have to wait for the next signal on the semaphore.
pParam | Pointer to bool (isCleanThreadGo) that tell the function to exit (close the thread). |
Definition at line 533 of file DcLoginManager.cpp.