Login with KLone

babongo edited this page Oct 5, 2012 · 1 revision

Login with KLone

by Radek Hnilica

This short text is about how to make login in your KLone application. The code is taken from my application sawid, so there are #include “sawi.h” all arround in the files. I also made some modification to translate messages from original language to english. The code as is is there not tested. The login works, but the logout not yet. Please excuse my English, I wrote this in hurry and didn’t check the language.

First of all, the logic

How you recognize your user is logged in? I choose the session for this. As a sign of logged user I use “username” in the session. If user is logged, then username contains the login name of the user. If user is not logged in, then no username is defined in session.

How to control user access to some of the pages? This is a crucial point. I do not want the user allways log in. I want him to brows the application and freely use to the point she want access some protected page. So the login mechanism exports macro REQUIRE_AUTH for this case. Every page which have to be guarded by login procedure uses this macro on the top

<%
    REQUIRE_AUTH(SCRIPT_NAME);
    ...
%>

This macro expands to function call of login component. During this call the session.username is checked and if not defined, application is redirected to login.kl1 page. It also remember the actual page SCRIPT_NAME for the future return to this page.

How to let the user control her login? How to let her login and logout as she wish? I wrote component user.kl1 which can be embedded in any page. I choose to embed it in common template so this component is visible in every page. This component displays in two states. If the user is not logged it show it on the page and allow the user log in. On the other side if the user is logged, it displays login name of the user and allow the user to log out. So it renders: You are logged as: Radek. <Logout> or Your are not logged. <Login>.

The solution

Now how it’s done. Main code is in two components: login.kl1 and user.kl1. login.kl1 is normal page so it sits in the webapp/www directory. From the logic of the page. This page publish function login_force() which is called from macro REQUIRE_AUTH. Function checks if user is logged by reading “username” from session. If it is, nothing happens and program flows the normal way. But if username doesn’t exist in session, thus user is not logged. Then a current page name given in scrip argument is set into session.after_login. This is because wee need to know to which page we have to return after successful login. The the program flow is redirected to login.kl1 page.

Page login.kl1 displays login form. This form asks for the username and password. The user fill this and press the login button. The page reloads with username and password defined in request arguments. These are detected and checked if they are right. If yes, username is written into session and program flow is redirected to the original page noted in session.after_login.

webapp/www/login.kl1:

<%! /* -*- mode:c;coding:utf-8; -*- */
/*  
 * $Id: login.kl1,v 1.2 2007/12/21 09:28:10 radek Exp $
 * Login page.
 * Copyright (c) 2007 Radek Hnilica
 * License: GPL or WTFPL by your choice
 */
#include "sawi.h"
RCSID("$Id: login.kl1,v 1.2 2007/12/21 09:28:10 radek Exp $");

/*
 * If user not logged, force login.
 */
void login_force(session_t *ss, request_t *rq, response_t *rp, const char *script) 
{
    char *username;

    username = session_get(ss, "username");        if (!username) {
        session_set(ss, "after_login", script);
        response_redirect(rp, "login.kl1");
    }   
} /* force_login() */

%><%

struct {
    char *username;
    char *password;
    char *destination;
} login;

login.username = request_get_arg(request, "username");
login.destination = session_get(session, "after_login");

if (!login.destination) login.destination = "index.kl1";

%>
<html>
  <head>
    <title>SAWI Login</title>
    <link rel="stylesheet" href="kl.css" type="text/css" />
    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  </head>

  <body>
    <div align="center">
    <h1>SAWI Login</h1>
    <p>Please give your user name and password to this program.</p>

    <%

switch (vars_get_value_i(request_get_args(request), "action"))
{
case 1:
        login.username = request_get_arg(request, "username");
        login.password = request_get_arg(request, "password");

    if (login.username && strlen(login.username)
        && login.password && strlen(login.password)) {

        /*
                 * authenticate here.  Check the login.username and login.password in the database for instance.
                 */
        session_set(session, "username", login.username);
        response_redirect(response, login.destination);
        session_del(session, "after_login");

        /*io_printf(out, "Do something with (username, password):" \
          " (%s,%s)", username, password);*/
    } else {
        io_printf(out, "Bad (username, password)");
    }
}
%>
    <form action="<%= SCRIPT_NAME %>" method="post">
      <table rules="all" border="1">
        <tr>
          <th>username:</th>
          <td><input type="text" name="username"/></td>
        </tr>
        <tr>
          <th>password:</th>
          <td><input type="password" name="password"/></td>
        </tr>
        <tr>
          <td colspan="2"><input type="submit" value="login"/></td>
        </tr>
      </table>
      <input type="hidden" value="1" name="action"/>
    </form>

    <hr/>
    <font size="-1">
      Copyright (c) 2007 by Radek Hnilica &mdash;
    </font>
  </div>
  </body>
</html>

There is also header file to this page

include/login.h

/*
 * $Id:$
 * Declarations to the login.kl1 page.
 * Copyright (c) 2007 Radek Hnilica
 * License: GPL or WTFPL by your choice
 */
#ifndef _LOGIN_H_
#define _LOGIN_H_
#include <klone/session.h>

#define REQUIRE_AUTH(self)  login_force(session, request, response, self)

void login_force(session_t *ss, request_t *rq, response_t *rp, const char *script);
#endif

To allow see which user is logged in and allow her to logout I wrote user.kl1 component. This component is not a page so it resides in components directory.

component/user.kl1:
/*
 * $Id:$
 * User component.  Display username and allows login ang logout.
 * Copyright (c) 2007 Radek Hnilica
 * License: GPL or WTFPL by your choice
 */
#include "sawi.h"
#include "login.h"

#include <u/libu.h>    %><%
/*
 * Component variables.  Should be in separate structure to do not mix
 * with other variables and other component variables.
 */ 
struct {
    char *username;
    char *logout;
    char *login;
} user_kl1;

user_kl1.username = session_get(session, "username");
user_kl1.logout = request_get_arg(request, "logout");
user_kl1.login = request_get_arg(request, "login");

dbg("Before test of logout command.");
if (user_kl1.logout) {
    if (strcmp(user_kl1.logout, "1") == 0) {
        dbg("Before deleting username from sesion.");
        dbg_if(session_del(session, "username"));
        dbg("After deleting username from sesionn add before sesion save");
        dbg_if(session_save(session));
        dbg("After session save.");
        dbg_if(session_clean(session));
        dbg_if(session_save(session));
        user_kl1.username = NULL;
    }
} else if (user_kl1.login) {
    if (strcmp(user_kl1.login, "1") == 0) {
        REQUIRE_AUTH(SCRIPT_NAME);
    }
} 

%>
<% if (user_kl1.username) { %>
  You are <%= user_kl1.username %> <a class="command" href="<%=SCRIPT_NAME%>?logout=1">Logout</a>
<% } else { %>
  You aren't logged, <a class="command" href="<%=SCRIPT_NAME%>?login=1">Login</a>       
<% } %> 

(DEBUG: request.arg.logout=<%=user_kl1.logout%>; session.username=<%=session_get(session,"username")%>)

To test this component and show how to use it I wrote page:

<%! /* -*- mode:c;coding:utf-8; -*- */
/*
 * $Id: test_user.kl1,v new 2007/12/22  radek Exp radek $
 *
 * Copyright (c) 2007 Radek Hnilica
 */
#include "sawi.h"

RCSID("$Id: test.kl1,v 1.2 2007/12/21 16:24:35 radek Exp radek $");    #define TITLE "Test component/user.kl1"
%>
<%
//REQUIRE_AUTH(SCRIPT_NAME);
  /* Set the page variables before including pre_content. */
%>
<html>
  <head>
    <title><%=TITLE%></title>
    <link rel="stylesheet" href="kl.css" type="text/css" />
    <meta http-equiv="content-type" content="text/html;charset=UTF-8" />
  </head>
  <body>
    <h1><%=TITLE%></h1>
    <ul>
      <li><a href="<%=SCRIPT_NAME%>">Reload</a></li>
    </ul>
    <p>Component:<p>
    <hr/>
    <%@include ../../components/user.kl1%>
    <hr/>
  </body>
</html> 

Problems

  • The logout in components/user.kl1 doesn’t work.

Planed changes

Extend the login.kl1 component with access domain informations. So we can do at top of guarded pages something like:

REQUIRE_AUTH("admin", SCRIPT_NAME)

– Radek