CodeIgniter Forums
Help with beginner login questions - Printable Version

+- CodeIgniter Forums (https://forum.codeigniter.com)
+-- Forum: Archived Discussions (https://forum.codeigniter.com/forumdisplay.php?fid=20)
+--- Forum: Archived Development & Programming (https://forum.codeigniter.com/forumdisplay.php?fid=23)
+--- Thread: Help with beginner login questions (/showthread.php?tid=16709)

Pages: 1 2 3


Help with beginner login questions - El Forum - 03-16-2009

[eluser]Flying Fish[/eluser]
@jedd

you mentioned sharing some of your code a couple of posts back

I think I could use the help if you don't mind of course :-)

I don't think security will be an issue, as I'm not dealing with any sensitive info


Help with beginner login questions - El Forum - 03-16-2009

[eluser]jedd[/eluser]
[quote author="Flying Fish" date="1237235544"]the logout function should destroy the whole session[/quote]

Is that a requirement? For my stuff I just test if one session variable is set or not, and looking at the code you posted earlier this library works similarly - it looks for a 'logged_in' session var - this alone determines if you're logged in or not.

Destroying the entire session is one way of removing that session variable, but it seems an extreme response.

Can you query the author of your library on the right way of using it?


Help with beginner login questions - El Forum - 03-16-2009

[eluser]Flying Fish[/eluser]
Perhaps destroying the session is a bit extreme. It was just the way the user get's logged out in the library.

I think I've got it working. Instead of loading the view I actually needed to redirect back the welcome screen

this is the logout function in the class
[code]
function logout()
{
$this->authentication->logout();

redirect('/welcome/' , 'refresh');
}
[code]

this is the logout method in the authentication library
[code]
function logout() {
$CI =& get_instance();

//Destroy session
$CI->session->sess_destroy();
}

[code]


Help with beginner login questions - El Forum - 03-16-2009

[eluser]jedd[/eluser]
To paraphrase the great man himself .. Hell is other people's code. So take this with the same caution you took the other auth library. Wink

My model is straightforward and is (substantively) as follows. Note that this model fronts (or will front) all my authentication checks against the DB, as well as handling all member information - this means it'll watch over many tables, ultimately.

Code:
<?php
    /** =========================================================================
    * Model - Member
    *
    * Interface to the people - members, auth system, groups, roles
    *
    * Relied upon primarily by:
    *    the @link People Controller
    */

class  Member  extends Model {
    /** =========================================================================
    *   Attributes
    **/
    // Don't think we'll use any - too often we'll be talking about
    // non-currently-logged-in user through this model.

    /** =========================================================================
    * Constructor
    */
    function  Member ()  {
        parent::Model();
        }

    /** =========================================================================
    * Authenticate by user/pass
    *
    * Will populate the session user data at the same time.
    *
    * @access public
    * @param $in_user        supplied by user
    * @param $in_pass        supplied by user (original, unencrypted)
    * @return boolean
    */
    function  authenticate  ($in_name = NULL , $in_pass = NULL)   {
        if ( ($in_name == NULL) OR ($in_pass == NULL) )
            return FALSE;
        $query = $this->db->query ('SELECT member.id, admin, password, login_name,
                                            theme.name as theme
                                    FROM member, theme
                                    WHERE member.login_name="' . $in_name .'"
                                    AND member.theme = theme.id');

        if ($query->num_rows() != 1)
            return FALSE;

        // Okay, we've matched username, now test password
        $result = $query->row_array ();
        if ($result['password'] == md5($in_pass))  {
            // Could do the entire array, but I prefer to track & minimise what session data is used
            $this->session->set_userdata ('login_name', $result['login_name']);
            $this->session->set_userdata ('admin', $result['admin']);
            $this->session->set_userdata ('theme', $result['theme']);
            return TRUE;
            }
        else
            return FALSE;
        } // end-method authenticate()

    /** =========================================================================
    * Logout
    *
    * Exactly what it sounds like.
    * o  removes session data
    * o  updates database with logout time
    *
    * Undecided whether to use this function to allow admin to log someone else out ... ?
    *
    * @access public
    * @param  $login_name    supplied, but could be assumed ..?
    * @return boolean
    */
    function  logout  ($login_name = NULL)   {
        if ( ($login_name == NULL) )
            $login_name = $this->session->userdata('login_name');

        if (! $login_name)
            return FALSE;

        // @TODO - database updates - last login, etc.

        $this->session->unset_userdata ('login_name');
        $this->session->unset_userdata ('admin');
        $this->session->unset_userdata ('theme');
        return TRUE;
        } // end-method authenticate()
Caveats for the above include: Work in progress, Theme management can be ignored, Architecture of using model as real object (replete with attributes) still being considered (see current thread elsewhere), Code snippet does is not complete (things like lookup_x, update_y, etc - not included).


Database schema FYI:
Code:
CREATE TABLE member (                           # the USER table, by any other name.
    id                  SERIAL,
    appellation         bigint,                 # FK to appellation table (later)
    login_name          char(50) UNIQUE,
    first_name          char(50),
    surname             char(50),
    password            char(32),               # MD5, of course
    last_login          datetime,
    created             datetime,
    theme               int,                    # FK to theme table (later)
    admin               BOOLEAN DEFAULT '0',    # LATER - work out a better ACL / Permission system
    INDEX (login_name),
    PRIMARY KEY (id)
    );

# Test data
INSERT INTO
    member  (login_name, appellation, first_name, surname, password, created, theme, admin)
    VALUES (
        "admin",
        (SELECT id FROM appellation  WHERE text="Prince Regent"),
        "Administrator",
        "Administrator",
        MD5("secret"),
        now(),
        (SELECT id FROM theme WHERE name="grey"),
        1
        );

Caveats on schema - Work in progress, Theme table contains id/name/longname/description, Appellation contains id/text. These can be ignored, but may be of relevance if you're heading in a similar direction. Other tables are yet to be produced but obvious early candidate is some way to log activity.



Controller code to follow.


Help with beginner login questions - El Forum - 03-16-2009

[eluser]jedd[/eluser]
Controller code looks like this:

Code:
<?php
    /** =========================================================================
    * Controller - People
    *
    * To view information about people.
    *
    * @author jedd
    * @package pdb
    * @version v0.9
    *
    */

class  People  extends  MY_Controller  {
    /// Attributes


    /** =========================================================================
    * Constructor
    **/
    function People()  {
        parent::MY_Controller();
        /// Models
        $this->load->model  ('member');

        /// Helpers
        $this->load->helper ('url');
        $this->load->helper ('html');


        /// Debug profiling
        // $this->output->enable_profiler(TRUE);

        /// Stuff we want to do for all these methods

        } // end-constructor  People()



    /** =========================================================================
    * @TODO - an entry page for people probably makes sense.
    *
    *            Or redirect to 'me' ?
    */
    function index() {
        $this->data['main_text_view'] = "You shouldn't be here";
        $this->load->view ('default', $this->data);
        }

    /** =========================================================================
    * Login
    *
    * Exactly what it sounds like.
    *
    * @access public
    * @param  none (other than search form results)
    * @return no idea yet
    */
    function login ( )  {
        /// Method-specific libraries
        $this->load->library('form_validation');
        
        /// Method-specific helpers
        $this->load->helper('form');

        /// Rules for user and password fields
        $this->form_validation->set_rules('in_login', 'User name',
                                            'trim|required|min_length[3]|xss_clean');
        $this->form_validation->set_rules('in_pass', 'Password',
                                            'trim|required|min_length[3]|xss_clean');

        /// If the user's already logged in, offer them a choice:
        if ($current_login_name = $this->session->userdata('login_name'))  {
            // This is where we test if we're on a redirect() - so show top-bar-div username.
            if ($this->session->flashdata("just_logged_in") )
                $this->data['main_text_view']  = "Welcome back, <b>". $current_login_name ."</b>";
            else
                $this->data['main_text_view'] = "You are already logged in as <b>".
                        $current_login_name ."</b><br /><br />You need to ".
                        anchor ("people/logout", "logout") ." before logging in as someone else.<br />";
            }
        else  {
            if ($this->form_validation->run())  {  // Valid form, so we try to authenticate
                $in_login = $this->input->post('in_login');
                $in_pass  = $this->input->post('in_pass');
                $valid_user_pass = $this->member->authenticate ($in_login, $in_pass);
                if ($valid_user_pass)  {  // Hey - we're all good!
                    // This thing forces a refresh to ensure the top-nav-bar shows username.
                    $this->session->set_flashdata('just_logged_in', 'true');
                    redirect ("people/login");
                    }
                else
                    $this->data['main_text_view'] = "Incorrect user / pass.<br>You can ".
                                                     anchor ("people/login", "try again");
                }
            else  {  // Invalid form, so we re-try the login page
                $data['login_form_data'] = $this->_generate_login_form_data( );
                $this->data['main_text_view']
                         = $this->load->view ('people/create_login_form', $data, TRUE);
                }
            }

        /// @TODO - limit the number of retries somehow
        $this->load->view ('default', $this->data);

        }  // end-method login()



    /** =========================================================================
    * Logout
    *
    * Exactly what it sounds like.
    *
    * @access public
    * @param  none (other than search form results)
    * @return no idea yet
    */
    function logout ( )  {
        if ($this->session->userdata('login_name'))  {
            $this->member->logout('login_name');
            }
            
        // This is to force one refresh - to force top-bar to not show residual login_name
        if ($this->session->flashdata('just_logged_out'))
            $this->data['main_text_view'] = "You are logged out - Bye! <br /><br />Do you want to ".
                                            anchor ("people/login", "login") . " again?";

        else {
            $this->session->set_flashdata('just_logged_out', 'true');
            redirect ('people/logout');
            }
        $this->load->view ('default', $this->data);
        }  // end-method login()


Caveats include: Work in progress, Flashdata approach is a bit ugly and not sure on a preferred way of handling this (the redirect to a secondary method approach on valid-form seems a bit ugly too).

Again out of space, so shall push view into the next message.


Help with beginner login questions - El Forum - 03-16-2009

[eluser]jedd[/eluser]
Oh, form data is an array I generate (and I'm pretty consistent with it - makes it cleaner for me to pass data to forms), and here's the stuff from the end of the controller I posted first. You'd be just as well with this stuff done hard coded into your view I reckon.

Code:
/** =========================================================================
    * Create the login form data (array)
    *
    * An array only because the template (organism/search) was, and we might
    * one day put in a 'forgot password' secondary form on this same page.
    *
    * @access private
    * @param  $posted_values (array) - value to re-insert into form  (UNUSED with login)
    * @return $search_form_data (array)
    */
    private function  _generate_login_form_data ( )  {
        $form_data = array (
            array (
                "preamble" => "",
                "target"   => "people/login",
                "input"    => array (
                    "Username" => array (
                        "name"  => "in_login",
                        "value" => "",
//                        "tabindex" => "0",        // this seems to break tabbing between user/pass
                        ),
                    "Password" => array (
                        "name"  => "in_pass",
                        "value" => "",
//                        "tabindex" => "1",        // this seems to break tabbing between user/pass
                        ),
                    ),
                "hidden" => array (
                    "name"  => "unused",
                    "value" => "unused",
                    ),
                "submit" => array (
                    "button" => "Login",
                    "name"   => "login",
                    ),
                 ),
            
            );
        return $form_data;
        }



And a view of my login form (snippet only as I embed view-loads back into a main view).
Code:
&lt;?php

/**
*    create_login_form
*
*    Generates the very basic user/pass form required to login.
*
*    @param $array_of_values        array with user & pass
*
*/

    // Let the user know if something weird happened.
    echo "<div class=\"validation_errors\">". validation_errors() . "</div>";
    if (validation_errors())
        echo "<hr>";
?&gt;


<h2>
Log in to the PDB using your existing credentials
</h2>

<hr>

&lt;?php

    foreach ($login_form_data as $form)  {
        echo "\n". form_open ($form["target"]);
        echo "\n". $form["preamble"] . " &nbsp; &nbsp; ";
        foreach ($form["input"] as $input_preamble=>$input_data)  {
            echo "\n". $input_preamble . " : ";
            echo form_input ($input_data);
            echo " &nbsp; &nbsp; &nbsp; ";
            }
        echo "\n". form_hidden ($form["hidden"]["name"], $form["hidden"]["value"]);
        echo "\n". form_submit($form["submit"]["name"], $form["submit"]["button"]);
        echo "\n". form_close ();
        echo "<hr>";
        }

Caveats -- you can probably guess. Smile


Help with beginner login questions - El Forum - 03-16-2009

[eluser]Flying Fish[/eluser]
Thanks jed,

I really appreciate you posting this

I'm going to looking it over.


Help with beginner login questions - El Forum - 03-16-2009

[eluser]jedd[/eluser]
Quote:I don't think security will be an issue, as I'm not dealing with any sensitive info

Oh, security caveats .. most importantly, research this stuff before you go live anywhere with real data.

I think that I am mostly covered by changing config.php settings, specifically sess_encrypt_cookie and the encryption_key.


Help with beginner login questions - El Forum - 03-16-2009

[eluser]jedd[/eluser]
Sorry .. one last code dump, promise.

In case it's not obvious, you would utilise all this gumpf, in your controller code, with something like:

Code:
if ($this->session->userdata('login_name')
    // stuff here that relies upon user being authenticated
else
    // and if they're not ...

Out of curiosity I made up a helper - not sure if I'll use it - but I expect it could be handy if a) I create a range of helper functions, say with a common prefix, such that I can use this approach in other applications later, and b) it might be handy for tests that are more complex than just seeing if one bit of of userdata is in the session. Oh, and possibly © it might result in more readable code.

Here's my helper proof of concept - almost totally pointless, but you get the idea.

I think that the =& get_instance() call is inexpensive (it's just a pointer, not a copy, AFAIK).

Code:
&lt;?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');

/**
***  PDB helper - lots of tiny functionettes.
**/

function is_logged_in()  {
    $CI =& get_instance();
    return ($CI->session->userdata('login_name')) ? TRUE : FALSE ;
    }

function is_admin()  {
    $CI =& get_instance();
    return ($CI->session->userdata('admin')) ? TRUE : FALSE ;
    }

/* End of file pdb_helper.php */
/* Location: ./system/application/helpers/pdb_helper.php */



Help with beginner login questions - El Forum - 04-09-2009

[eluser]M4rc0[/eluser]
Your helper above is very usefull jedd

I'm using it instead of extending the class (unnecessary, so far..)

I also hope that =& get_instance() is inexpensive Tongue

Thanks for sharing!