1

Using Auth module in Kohana 3.2

This post is shown incomplete example for using Auth module in Kohana 3.2

  1. Besides installing Kohana as normal. bootstrap.php has be modified in order to enable these modules that are Auth, Database and Orm. File-based authentication seems to be available also but this post will be focus on using orm and database only.

    Kohana::modules(array(
        'auth'       => MODPATH.'auth',       // Basic authentication
        // 'cache'      => MODPATH.'cache',      // Caching with multiple backends
        // 'codebench'  => MODPATH.'codebench',  // Benchmarking tool
        'database'   => MODPATH.'database',   // Database access
        // 'image'      => MODPATH.'image',      // Image manipulation
        'orm'        => MODPATH.'orm',        // Object Relationship Mapping
        // 'unittest'   => MODPATH.'unittest',   // Unit testing
        // 'userguide'  => MODPATH.'userguide',  // User guide and API documentation
        ));
    
  2. Provide configuration files in application/config. There should be auth.php and database.php copied from /modules.
  3. Create a database using SQL file named auth-schema-mysql.sql, which is included in Kohana 3.2. ( I made a mistake by spending time to find it using Google. ). Then change database.php to conform your database.
  4. Create application/view/template/default.php

    <?php echo $content;?>
    
  5. Create application/view/pages/login.php
    <?php echo form::open('/user/login'); ?>
     <?php echo form::label('username', 'Username:'); ?>
     <?php echo form::input('username'); ?>
     <?php echo form::label('password', 'Password:'); ?>
     <?php echo form::password('password'); ?>
     <?php echo form::submit('submit', 'Login'); ?>
    <?php echo form::close(); ?>
    

    (This is based on http://docs.kohanaphp.com/addons/auth#mysql_schema

  6. Create a user controller by inheriting Template controller as follow
    <?php defined('SYSPATH') or die('No direct script access.');
    
    class Controller_User extends Controller_Template
    {
     public $template = 'templates/default';
    
     public function action_login() 
     {
     $this->auto_render = FALSE; 
     $post = $this->request->post();
     $success = Auth::instance()->login($post['username'], $post['password']);
    
     if ($success)
     {
     $this->response->body('OK');
     }
     else
     {
     $this->response->body('FAIL');
     }
     }
     
     public function action_login_form()
     {
     $this->template->content = View::factory('pages/login');
     }
     
    } 
    
    

    At this state, you must be able to access URL /user/login_form and try to login and it should always report FAIL

  7. Add a user by directly insert a user to the table Users in MySQL. However the password must be generated from Auth::instance()->hash. Moreover users_roles must be added. Normally you should add 1,1.
  8. Everything should works now. However the controller still did not check whether the user is logged in. Register action is still not provided. I think what is left is not difficult to do by consulting an API reference.

Tags: , , , , , ,

0

Kohana CLI (2)

หลังจาก เรียกใช้ Kohana จาก command line interface ได้แล้ว แต่ทีนี้อยากใส่ค่าตัวแปรอะไรเข้าไปด้วย ก็ทำง่ายๆ เลยมันมี option –get กับ –post อยู่

ยกตัวอย่างเช่น ผมมี controller ชื่อ home และ action คือ index และอยากส่งค่า x=10 และ y=abc เข้าไปทาง post method ก็ทำแบบนี้

php index.php –uri=”home/index” –post=”x=10&y=abc”

เสร็จละ

0

Laemya – A lightweight web framework

ช่วงนี้ผมเขียน PHP ปกติใช้ CakePHP แต่คราวนี้อยากได้อะไรที่บางกว่า CakePHP สักหน่อย แต่ว่าก็ยังติดใช้ CakePHP อยู่ทำไปทำมาก็เลยก็โปรแกรมอะไรบางอย่างออกมา ไม่รู้ว่าเรียกว่า web framework ได้เปล่า แต่ก็แยกมันออกมาและตั้งชื่อว่า “แหลมหญ้า” (laemya) ตามเขาแหลมหญ้าอุทยานแห่งชาติที่ผมไปเที่ยวบ่อยที่สุด

พยายามจะเขียนเป็นแบบ MVC แต่ก็คงไม่ค่อยเหมือนเท่าไหร่ ไม่ได้อาศัพพวก mod_rewrite หลักๆ แล้วก็ยังใช้การไปเรียกไฟล์ตรงๆ เหมือนใช้ PHP ปกติ แต่ว่า มี code บนหัวไฟล์นิดหน่อยที่จะไปโหลดพวก controller และ model มาให้ แบบนี้ เช่นผมจะสร้างเว็บที่ http://localhost/user/laemya.php ผมก็ไปสร้างไฟล์ user/laemya.php ขึ้นมาแบบนี้

<?php
    require_once "../laemya.php";
    laemya_init(__FILE__);
?>
<?php function title() { return "Laemya test page"; } ?>
<?php laemya_header() ?>
<div>
<?php
    print_r($data);
?>
</div>
<?php laemya_footer() ?>

โปรแกรม 2 บรรทัดข้างบนจะไปสร้าง controller และสร้าง model ยัดใส่ controller ไว้ ส่วน $data ที่ไม่รู้มาจากไหนก็คือผลลัพธ์ที่คืนมาจาก controller

ก็เลยต้องไปสร้าง model รอไว้ก่อน ใน model/user.php

<?php
    class User extends AppModel {
        var $table = 'users';

        function __construct() {
            parent::__construct();
        }
}
?>

model นี้ก็ไม่ได้ถึงกับเป็น orm หรอก ก็แค่ไปเรียกใช้ mdb2 อีกที แต่ก็มีตัวช่วยบ้าง ตามที่ผมจะอยากใช้

ตาราง users หน้าตาประมาณนี้

mysql> desc users;
+----------+--------------+------+-----+---------+----------------+
| Field    | Type         | Null | Key | Default | Extra          |
+----------+--------------+------+-----+---------+----------------+
| id       | int(11)      | NO   | PRI | NULL    | auto_increment | 
| username | varchar(255) | NO   | UNI | NULL    |                | 
| realname | text         | NO   |     | NULL    |                | 
| email    | varchar(255) | NO   | UNI | NULL    |                | 
| password | varchar(255) | NO   |     | NULL    |                | 
| group_id | int(11)      | NO   |     | NULL    |                | 
+----------+--------------+------+-----+---------+----------------+
6 rows in set (0.05 sec)

ทีนี้ก็ลองดู controller ชื่อ controller/user_controller.php

<?php
    class UserController extends Controller {
        function laemya() {
            return $this->User->findAll();          
        }
    }
?>

user/laemya.php มันก็จะมาเรียก method ชื่อ laemya ใน UserController เอง แล้ว UserController ก็ไป query database มาแบบง่ายๆ แล้วคืนผลไป

นอกจากข้างบนแล้ว laemya ก็ยังมี feature พวกนี้ (ที่ปกติ framework ก็มักจะมีกันหมด)

  • authentication – พอใส่ var $auth = true; ลงใน controller มันก็จะถูกเรียกใช้งาน ถ้าอยากให้ controller ถูก protect ก็สร้าง method ชื่อ before ใน controller แล้วเรียก $this->checkUser() ใน before
  • column แบบ json ใน model ใส่ var $jsonColumns = array(“ชื่อ column”) แล้ว model ก็จะแปลง json กับ php ให้เวลาสั่ง findAll กับ save
  • มี validator ด้วย โดยสร้าง method ชื่อ validate ใน model แล้ว return array ที่ประมาณ array(‘user’ => ‘is required’) ออกมา
  • กำหนดได้ว่าจะให้ใส่ model ไหนใน controller บ้างใช้ var $uses = array(‘ชื่อ model’, ‘ชื่อ model’ …);
  • พวก $this->redirect และ $this->flash อะไรแบบนี้ก็ใช้ได้ด้วย

อีกนิดหนึ่งคือ laemya ไม่ได้อ่าน scheme ของ database เวลาจะใช้ save ใน model ต้องบอกมันด้วยว่ามี column อะไร type อะไร แบบนี้

<?php
    class MyModel extends AppModel {
        var $table = 'mytable';
        var $jsonColumns = array('tree');
        var $columns = array('owner_id', 'tree');
        var $types = array('integer', 'text');

        function __construct() {
            parent::__construct();
        }
    }
?>

ถ้าอยาก query ซับซ้อนขึ้นหน่อยก็ต้องเขียน SQL

<?php
    class User extends AppModel {
        var $table = 'users';

        function __construct() {
            parent::__construct();
        }
        
        function auth($email, $password) {
            $types = array('text', 'text');
            $password = md5($password);
            $sth = $this->db->prepare("select id from " . 
                $this->table . 
                " where email=? and password=?", $types);
            $data = array($email, $password);
            $result = $sth->execute($data);
            if (PEAR::isError($result)) 
                die($result->getMessage());
            $row = $result->fetchRow(MDB2_FETCHMODE_ASSOC);
            return $row;
        }
    }
?>

หลักแล้วก็ใช้ mdb2 แต่ว่าต่อไปก็อาจจะมีตัวช่วยเพิ่มขึ้น database ต่อที่ไหน ต้องไปแก้ใน cfg/config.php และพร้อม base url …

ถ้าทราบว่ามีโครงการไหนทำแนวๆ นี้อยู่แล้วช่วยบอกผมด้วยนะครับ เผื่อจะเลิกทำเอง

ลืมบอกว่า project อยู่ที่ http://laemya.sourceforge.net/ ตอนนี้ยังไม่มี package ครับ แต่ว่าสามารถโหลดได้โดย

hg clone http://laemya.hg.sourceforge.net:8000/hgroot/laemya/laemya 
0

AuthKit + OpenID + MysqlStore

In previous examples, I always use file store, which I found that it is quite difficult to use when I deploy BasaAsa to the server so I change to use MySQL store. To use MySQL store, I modified websetup.py in order to create table for OpenID library.

websetup.py:

"""Setup the basaasa application"""
import logging

from basaasa.config.environment import load_environment
from basaasa.users.authkit_elixir_driver import UsersFromDatabase
from basaasa import model
from authkit.authenticate.open_id import make_store #added
from pylons import config #added

log = logging.getLogger(__name__)

def setup_app(command, conf, vars):
    """Place any commands to setup basaasa here"""
    load_environment(conf.global_conf, conf.local_conf)
    from elixir import metadata
    metadata.create_all(checkfirst=True)
    store_type = config.get('authkit.openid.store.type') #added
    stote_config = config.get('authkit.openid.store.config') #added
    conn, cstore = make_store(store_type, stote_config) #added
    if store_type == 'mysql': #added
        cstore.createTables() #added

Then after I run paster setup-app development.ini, tables for storing OpenID information have been created.

2

OpenID + AuthKit + Pylons (simple)

I try to post simple example how to use OpenID with AuthKit 0.4.3 and Pylons 0.9.7. by adapting example from AuthKit. So I just create new Pylons app named “topenid” and modify it as follow:

  1. development.ini:
    These lines below are added to development.ini

    authkit.setup.enable = true
    authkit.setup.method = openid, cookie
    authkit.openid.store.type = file
    authkit.openid.store.config =
    authkit.openid.charset = UTF-8
    authkit.openid.path.signedin = /auth/signin
    authkit.cookie.signoutpath = /auth/signout
    authkit.openid.store.baseurl = http://localhost:5000
    authkit.cookie.secret = somesecret
    
  2. midddleware
    ...
    import authkit.authenticate # this line is added
    ...
        app = PylonsApp()
        app = authkit.authenticate.middleware(app, app_conf) # this line is added
    ...
  3. topenid/controllers/auth.py
    import logging
    
    from pylons import request, response, session, tmpl_context as c
    from pylons.controllers.util import abort, redirect_to
    from authkit.permissions import RemoteUser
    from authkit.authorize.pylons_adaptors import authorize
    
    from topenid.lib.base import BaseController, render
    
    log = logging.getLogger(__name__)
    
    class AuthController(BaseController):
        def index(self):
            return 'Hello World'
    
        @authorize(RemoteUser())
        def test(self):
            return 'test'
    
        def signout(self):
            return 'signout'
    
        def signin(self):
            return 'signed in'

These are all modifications. I can access http://localhost:5000/auth/index as expect and http://localhost:5000/auth/test is redirected to an OpenID signin page. And I can sign out by visiting http://localhost:5000/auth/signout.

This example seems to work well but I need something more complex for example every user doesn’t have the same role. So I may post another more complex example soon.

1

Authkit + Elixir

ใช้ pylons 0.9.7rc3 แต่ว่าใช้ elixir 0.6.1 มาครอบ sqlalchemy อีกที แทนที่จะใช้ sqlalchemy ตรงๆ ซึ่งเป็นวิถีทางที่เป็นที่นิยมกว่า ก็ไม่ได้ลำบากยากเย็นอะไร แก้ให้ใช้ elixir ก็ทำตาม ที่เคยๆ เขียนไว้ไม่มากก็เป็นอันใช้ได้ แต่พอมาเจอ authkit 0.4.2 ก็เป็นท่ายากขึ้นไปอีกนิดเหมือนกัน เพราะว่าไม่มี driver สำหรับ elixir ติดมาด้วย. ลองเอา driver ที่ http://wiki.pylonshq.com/display/pysbook/Authentication+and+Authorization มาก็ยังใช้ไม่ได้. ก็เลยเขียนใหม่ซะโดยใช้วิธีคล้ายกับของเดิมแต่ว่าเอา sqlalchemy 0.5.x driver มาแก้ แถมด้วยแก้ c-tor ของ entity ด้วย ทำให้ใช้กับ code เก่าง่ายขึ้น นอกจากนั้นก็เอา session.add(…) ที่ไม่จำเป็นสำหรับ elixir ออกซะ (ใช่มะถ้าผมเข้าใจไม่ผิด). elixir driver for authkit ก็เขียนแบบข้างล่างครับ ยาวหน่อย

# Elixir Driver for AuthKit
# Based on the SQLAlchemy 0.5 driver
# authkit_elixir_driver.py
# modified by Vee Satayamas
#
# Copyright (c) Copyright 2005-2007 James Gardner 
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.

from sqlalchemy import *
from paste.util.import_string import eval_import
from authkit.users import *

from sqlalchemy.orm import *
from elixir import *

class UsersFromDatabase(Users):
    """
    Database Version
    """
    def __init__(self, model, encrypt=None):
        print "!!! Driver"
        if encrypt is None:
            def encrypt(password):
                return password
        self.encrypt = encrypt
        if isinstance(model, (str, unicode)):
            model = eval_import(model)
        if hasattr(model, 'authkit_initialized'):
            raise AuthKitError(
                'The AuthKit database model has already been setup'
            )
        else:
            model.authkit_initialized = True

        # Update the model
        self.model = self.update_model(model)
        self.meta = self.model.meta

    def update_model(self, model):
        # following methods from a comment at
        # http://wiki.pylonshq.com/display/pysbook/Authentication+and+Authorization

        class User(Entity):
            uid = Field(Integer, primary_key=True)
            username = Field(String(255), unique=True, nullable=False)
            password = Field(String(255), nullable=False)
            group = ManyToOne("Group")
            roles = ManyToMany("Role", lazy=True) #"roles": relation(Role, lazy=True, secondary=users_roles_table),
            group = ManyToOne("Group")

            def __init__(
                self,
                username,
                uid=None,
                password=None,
                group_uid=None,
            ):
                Entity.__init__(self)
                self.uid         = uid
                self.username   = username
                self.password   = password
                self.group_uid  = group_uid

            def __repr__(self):
                return "User(%(username)s)" % self.__dict__

        class Group(Entity):
            uid = Field(Integer, primary_key=True)
            name = Field(String(255), unique=True, nullable=False)
            users = OneToMany("User")

            def __init__(self, name=None):
                Entity.__init__(self)
                self.name = name

            def __repr__(self):
                return "Group(%(name)s)" % self.__dict__

        class Role(Entity):
            uid = Field(Integer, primary_key=True)
            name = Field(String(255), unique=True, nullable=False)
            users = ManyToMany("User", lazy=True) #"users": relation(User, lazy=True, secondary=users_roles_table)

            def __init__(self, name=None):
                Entity.__init__(self)
                self.name = name

            def __repr__(self):
                return "Role(%(name)s)" % self.__dict__


        model.User = User
        model.Group = Group
        model.Role = Role
        setup_all()
        return model

    # Create Methods
    def user_create(self, username, password, group=None):
        """
        Create a new user with the username, password and group name specified.
        """
        if ' ' in username:
            raise AuthKitError("Usernames cannot contain space characters")
        if self.user_exists(username):
            raise AuthKitError("User %r already exists"%username)
        if group is None:
            new_user = self.model.User(
                username=username.lower(),
                password=self.encrypt(password)
            )
        else:
            if not self.group_exists(group):
                raise AuthKitNoSuchGroupError(
                    "There is no such group %r"%group
                )
            new_user = self.model.User(
                username=username.lower(),
                password=self.encrypt(password),
                group_uid=self.meta.Session.query(self.model.Group).\
                    filter_by(name=group.lower()).first().uid
            )
        self.meta.Session.flush()

    def role_create(self, role):
        """
        Add a new role to the system
        """
        if ' ' in role:
            raise AuthKitError("Roles cannot contain space characters")
        if self.role_exists(role):
            raise AuthKitError("Role %r already exists"%role)
        new_role = self.model.Role(role.lower())
        self.meta.Session.flush()

    def group_create(self, group):
        """
        Add a new group to the system
        """
        if ' ' in group:
            raise AuthKitError("Groups cannot contain space characters")
        if self.group_exists(group):
            raise AuthKitError("Group %r already exists"%group)
        new_group = self.model.Group(group.lower())
        self.meta.Session.flush()

    # Delete Methods
    def user_delete(self, username):
        """
        Remove the user with the specified username
        """
        user = self.meta.Session.query(self.model.User).filter_by(username=username.lower()).first()
        if user is None:
            raise AuthKitNoSuchUserError("There is no such user %r"%username)
        else:
            self.meta.Session.delete(user)
            self.meta.Session.flush()

    def role_delete(self, role):
        """
        Remove the role specified. Rasies an exception if the role is still in use.
        To delete the role and remove it from all existing users use
        ``role_delete_cascade()``
        """
        role = self.meta.Session.query(self.model.Role).filter_by(name=role.lower()).first()
        if role is None:
            raise AuthKitNoRoleUserError("There is no such role %r"%role)
        else:
            self.meta.Session.delete(role)
            self.meta.Session.flush()

    def group_delete(self, group):
        """
        Remove the group specified. Rasies an exception if the group is still in use.
        To delete the group and remove it from all existing users use ``group_delete_cascade()``
        """
        group = self.meta.Session.query(self.model.Group).filter_by(name=group.lower()).first()
        if group is None:
            raise AuthKitNoGroupUserError("There is no such group %r"%group)
        else:
            self.meta.Session.delete(group)
            self.meta.Session.flush()

    # Existence Methods
    def user_exists(self, username):
        """
        Returns ``True`` if a user exists with the given username, ``False``
        otherwise. Usernames are case insensitive.
        """
        user = self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first()
        if user is not None:
            return True
        return False

    def role_exists(self, role):
        """
        Returns ``True`` if the role exists, ``False`` otherwise. Roles are
        case insensitive.
        """
        role = self.meta.Session.query(self.model.Role).filter_by(
            name=role.lower()).first()
        if role is not None:
            return True
        return False

    def group_exists(self, group):
        """
        Returns ``True`` if the group exists, ``False`` otherwise. Groups
        are case insensitive.
        """
        group = self.meta.Session.query(self.model.Group).filter_by(
            name=group.lower()).first()
        if group is not None:
            return True
        return False

    # List Methods
    def list_roles(self):
        """
        Returns a lowercase list of all roll names ordered alphabetically
        """
        return [r.name for r in self.meta.Session.query(
            self.model.Role).order_by(self.model.Role.name)]

    def list_users(self):
        """
        Returns a lowecase list of all usernames ordered alphabetically
        """
        return [r.username for r in self.meta.Session.query(
            self.model.User).order_by(self.model.User.username)]

    def list_groups(self):
        """
        Returns a lowercase list of all groups ordered alphabetically
        """
        return [r.name for r in self.meta.Session.query(
            self.model.Group).order_by(self.model.Group.name)]

    # User Methods
    def user(self, username):
        """
        Returns a dictionary in the following format:

        .. code-block :: Python

            {
                'username': username,
                'group':    group,
                'password': password,
                'roles':    [role1,role2,role3... etc]
            }

        The role names are ordered alphabetically
        Raises an exception if the user doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        user = self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first()
        roles = [r.name for r in user.roles]
        roles.sort()
        return {
            'username': user.username,
            'group':    user.group and user.group.name or None,
            'password': user.password,
            'roles':    roles
        }

    def user_roles(self, username):
        """
        Returns a list of all the role names for the given username ordered
        alphabetically. Raises an exception if the username doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        roles = [r.name for r in \
            self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first().roles]
        roles.sort()
        return roles

    def user_group(self, username):
        """
        Returns the group associated with the user or ``None`` if no group is
        associated. Raises an exception is the user doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        return self.meta.Session.query(self.model.User).filter_by(username =
            username.lower()).first().group.name

    def user_password(self, username):
        """
        Returns the password associated with the user or ``None`` if no
        password exists. Raises an exception is the user doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        return self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first().password

    def user_has_role(self, username, role):
        """
        Returns ``True`` if the user has the role specified, ``False``
        otherwise. Raises an exception if the user doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        if not self.role_exists(role.lower()):
            raise AuthKitNoSuchRoleError("No such role %r"%role.lower())
        for role_ in self.meta.Session.query(self.model.User).filter_by(
                username=username.lower()).first().roles:
            if role_.name == role.lower():
                return True
        return False

    def user_has_group(self, username, group):
        """
        Returns ``True`` if the user has the group specified, ``False``
        otherwise. The value for ``group`` can be ``None`` to test that
        the user doesn't belong to a group. Raises an exception if the
        user doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        if group is not None and not self.group_exists(group.lower()):
            raise AuthKitNoSuchGroupError("No such group %r"%group.lower())
        user = self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first()
        if user.group is None:
            if group == None:
                return True
        else:
            if group is not None and user.group.name == group.lower():
                return True
        return False

    def user_has_password(self, username, password):
        """
        Returns ``True`` if the user has the password specified, ``False``
        otherwise. Passwords are case sensitive. Raises an exception if the
        user doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        user = self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first()
        if user.password == self.encrypt(password):
            return True
        return False

    def user_set_username(self, username, new_username):
        """
        Sets the user's username to the lowercase of new_username.
        Raises an exception if the user doesn't exist or if there is already
        a user with the username specified by ``new_username``.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        if self.user_exists(new_username.lower()):
            raise AuthKitError(
                "A user with the username %r already exists"%username.lower()
            )
        user = self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first()
        user.username = new_username.lower()
        self.meta.Session.flush()

    def user_set_password(self, username, new_password):
        """
        Sets the user's password. Should be plain text, will be encrypted using self.encrypt
        Raises an exception if the user doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        user = self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first()
        user.password = self.encrypt(new_password)
        self.meta.Session.flush()

    def user_set_group(self, username, group, auto_add_group=False):
        """
        Sets the user's group to the lowercase of ``group`` or ``None``. If
        the group doesn't exist and ``add_if_necessary`` is ``True`` the
        group will also be added. Otherwise an ``AuthKitNoSuchGroupError``
        will be raised. Raises an exception if the user doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        if not self.group_exists(group.lower()):
            if auto_add_group:
                self.group_create(group.lower())
            else:
                raise AuthKitNoSuchGroupError("No such group %r"%group.lower())
        user = self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first()
        user.group = self.meta.Session.query(self.model.Group).filter_by(
            name=group.lower()).first()
        self.meta.Session.flush()

    def user_add_role(self, username, role, auto_add_role=False):
        """
        Sets the user's role to the lowercase of ``role``. If the role doesn't
        exist and ``add_if_necessary`` is ``True`` the role will also be
        added. Otherwise an ``AuthKitNoSuchRoleError`` will be raised. Raises
        an exception if the user doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        if not self.role_exists(role.lower()):
            if auto_add_role:
                self.role_create(role.lower())
            else:
                raise AuthKitNoSuchRoleError("No such role %r"%role.lower())
        user = self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first()
        role = self.meta.Session.query(self.model.Role).filter_by(
            name=role.lower()).first()
        user.roles.append(role)
        self.meta.Session.flush()

    def user_remove_group(self, username):
        """
        Sets the group to ``None`` for the user specified by ``username``.
        Raises an exception if the user doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        user = self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first()
        user.group = None
        self.meta.Session.flush()

    def user_remove_role(self, username, role):
        """
        Removes the role from the user specified by ``username``. Raises
        an exception if the user doesn't exist.
        """
        if not self.user_exists(username.lower()):
            raise AuthKitNoSuchUserError("No such user %r"%username.lower())
        if not self.role_exists(role.lower()):
            raise AuthKitNoSuchRoleError("No such role %r"%role.lower())
        user = self.meta.Session.query(self.model.User).filter_by(
            username=username.lower()).first()
        for role_ in user.roles:
            if role_.name == role.lower():
                user.roles.pop(user.roles.index(role_))
                self.meta.Session.flush()
                return
        raise AuthKitError(
            "No role %r found for user %r"%(role.lower(), username.lower())
        )

ในกรณีตัวอย่างของผม ผมทำ project ชื่อ auth7 ขึ้นมาแล้วก็เอา driver ไปไว้ใน auth7/users ใน auth7/users ก็มีไฟล์ __init__.py (ตามแบบ python module ทั่วไป) และ authkit_elixir_driver.py ตามที่เขียนไว้ข้างบน

__init__.py ก็เขียนตามนี้

import authkit_elixir_driver

ใน development.ini ก็เพิ่มแบบนี้เข้ามา

authkit.setup.enable = true
authkit.setup.method = form, cookie
authkit.form.authenticate.user.type = auth7.users.authkit_elixir_driver:UsersFromDatabase
authkit.form.authenticate.user.data = auth7.model
authkit.cookie.secret = SomeSec
authkit.cookie.signoutpath = /auth/signout

ใน websetup ก็เขียนแบบนี้

import logging

from auth7.config.environment import load_environment
from auth7.users.authkit_elixir_driver import UsersFromDatabase

log = logging.getLogger(__name__)

def setup_app(command, conf, vars):
    load_environment(conf.global_conf, conf.local_conf)
    from auth7.model import meta
    from auth7 import model
    from elixir import metadata
    users = UsersFromDatabase(model)
    metadata.create_all(checkfirst=True)
    users.user_create("admin", password="password")

ลืมเขียนอะไรอีกหรือเปล่านะ :-P … ว่าจะ post ที่ blog.vee-u.com ไปสักพักก่อน ไว้แก้ๆ อีกสักนิดค่อยเอาลง pylons66.com ละกัน

2

Pylons + mod_wsgi + mod_userdir

ต่อจาก ติดตั้ง Django ก็ต่อด้วย Pylons. ก่อนหน้าจะพยายามลง Pylons ผมก็ลองพวก WSGIDaemonProcess ไปแล้ว แต่ว่าไม่สำเร็จก็เลยข้ามๆ ไป. ใช้ user เป็น www-data ก็ได้ :-P.

Pylons ก็มา pattern เดิมๆ ที่อ่านมาจาก http://code.google.com/p/modwsgi/wiki/IntegrationWithPylons เริ่มด้วยสร้าง pylons.wsgi ใน /home/veer/public_html/wsgi-scripts ตามนี้

APP_CONFIG = "/home/veer/Develop/pylons/mydb4/production.ini"
import os
os.environ['PYTHON_EGG_CACHE'] = "/home/veer/egg"
import sys
sys.path.append("/home/veer/Develop/pylons/mydb4")
from paste.deploy import loadapp
application = loadapp("config:" + APP_CONFIG)

แล้วก็สร้าง /home/veer/egg เปลี่ยน permission ( mkdir /home/veer/egg; chmod 777 /home/veer/egg )

แล้วก็แก้สร้าง production.ini โดยการ copy มาจาก development.ini แล้วก็แก้ให้
full_stack = false
และ
debug = false

เท่านี้ก็น่าจะพอใช้ได้แล้ว

ลืมไปผมสร้าง app ไว้ใน /home/veer/Develop/mydb4 นะครับ พวก development.ini ก็อยู่ในนี้ล่ะ

1

Django + userdir + mod_wsgi

แน่นอนเป็นภาคต่อมาจาก Turbogears + userdir + mod_wsgi. Django ก็ใช้ท่าคล้ายๆ Turbogears เปลี่ยน prefix นิดๆ หน่อยก็ใช้ได้แล้ว

หลังจากศึกษาวิธีใช้จาก http://code.google.com/p/modwsgi/wiki/IntegrationWithDjango ผมสร้าง django.wsgi ไว้ใน /home/veer/public_html/wsgi-scrips อีกเช่นเคย

import os, sys
sys.path.append('/home/veer/Develop/django/mysite') # site ของผมเก็บไว้ที่นี้
sys.path.append('/home/veer/Develop/django')
os.environ['DJANGO_SETTINGS_MODULE'] = 'mysite.settings'

import django.core.handlers.wsgi

application = django.core.handlers.wsgi.WSGIHandler()

ที่ลำบากนิดหน่อยคือพวก media ทั้งหลาย ผมก็ copy มาไว้ใน home เลย (ขนาดประมาณ 300KiB กว่าๆ)

cp -r /usr/share/python-support/python-django/django/contrib/admin/media/ /home/veer/public_html/django_admin_media

แล้วก็ไปแก้ settings.py ใน /home/veer/Develop/django/mysite นิดหน่อย แก้ ADMIN_MEDIA_PREFIX จากเท่ากับ ” เป็น ADMIN_MEDIA_PREFIX = ‘/~veer/django_admin_media/’

เท่านี้ก็ไปเปิดเว็บดูก็ใช้ได้ (ถ้าผมไม่ได้ลืมเขียนอะไรไปนะ)

พรุ่งนี้น่าจะได้ลอง Pylons กับ WSGIDaemonProcess username user=username อะไรทำนองนี้.