IAM

ARTICLE

A Kohana Demo Application

This article presents a Kohana demo application demonstrating the compatibility and use of several Kohana modules I wrote in the course of the last few years. The application is compatible to Kohana 3.3.6, 3.2 and 3.1 and available on GitHub and among others includes Kohana Red, an authentication module, Kohana Green, an access control module, and Gaps, a form generation and validation module.

While revisiting most of my Kohana modules, initially developed between 2011 and 2013, I decided to code a small application demonstrating the use of these modules. This also helped to update the modules to the latest version of Kohana (3.3.6 in this case) and demonstrate compatibility to older version (3.2 and 3.1). The demo application is available on GitHub:

Kohana Demo on GitHub

Modules

The applications is intended to demonstrate the usage of the following Kohana modules:

Red. A secure authentication module similar to kohana/auth and based on kohana/orm. Security features include user-dependent salts as well as application wide salt, iterative hashing, tokens to remember logins and prohibit frequent login attempts and the combination with the database session driver. A tutorial can be found in this blog entry.

Green. An access control module based on Red allowing to control access to models and controllers by organizing users in roles (e.g. "admin", "user" etc.). The access control rules are stored in the database and are easily extendable as discussed in this blog entry.

Blue. A user configuration module based on Red allowing to easily store and retrieve user-specific configuration options in the database. The user configuration options are stored serialized allowing to store structured data (such as arrays or objects). Through default values, the applications does not need to ensure that all configuration options are known when creating new users.

Yellow. Logging module based on Green allowing to log controller and model access by users. Time, user and controller/model is logged whenever the controller/model is accessed.

Gaps. A form generation and validation module based on kohana/orm. Based on a configuration method which is part of the model, the module automatically generates a form (the corresponding views can be adapted to the frontend framework used, e.g. Twitter Bootstrap), validates the use input and updates the model if validation passes.

Navigation. A navigation generation module allowing to programmatically create navigations, render them (based on configurable themes/views) and set user breadcrumbs.

Media. A media/asset module allowing to programmatically register CSS and JS files as well as their dependencies. The registered assets are then bundled, minified on the fly and written to the cache. This way, the bundled assets are only downloaded by the browser whenever the included files have changed.

Installation

The demo application is compatible with Kohana 3.3.6, 3.2 and 3.1. In order to install the application for a specific version, checkout the correct branch of the GitHub repository. For Kohana 3.3.6 this means:

$ git clone https://github.com/davidstutz/kohana-demo --recursive

The --recursive will make sure that all required modules as well as the kohana core are checked out. When using the branches for version 3.2 or 3.1, the modules are set up to track the corresponding versions, as well. In case individual modules to not correspond to the intended version (this can be checked via cd kohana-demo/modules/orm and git branch), the correct versions can easily be checked out afterwards:

# For orm and database:
$ cd kohana-demo/modules/orm
$ git checkout 3.2/master # or 3.1/master
# For the remaining modules:
$ cd kohana-demo/modules/red
$ git checkout 3.2 # or 3.1
# For kohana-core:
$ cd kohana-demo/system
$ git checkout 3.2/master # or 3.1/master

After checking out the desired branch, a database needs to be set up (note that I assume a Kohana compatible environment such as latest LAMPP/XAMPP on Ubuntu/Windows). The configuration of the demo application uses localhost/root as default connection with database kohana_demo and uses the MySQLi driver. Furthermore, the session is stored in the database, as well. The following SQL scheme should be applied to the new database:

-- -----------------------------------------------------
-- Table `users`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `users` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `email` VARCHAR(255) NOT NULL ,
  `first_name` VARCHAR(255) NOT NULL ,
  `last_name` VARCHAR(255) NOT NULL ,
  `password` VARCHAR(65) NOT NULL ,
  `salt` VARCHAR(255) DEFAULT NULL ,
  -- Additional fields can be added ...
  PRIMARY KEY (`id`) ,
  UNIQUE INDEX `uniq_email` (`email` ASC))
DEFAULT CHARACTER SET = utf8;

-- -----------------------------------------------------
-- Table `user_roles`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `user_roles` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `name` VARCHAR(32) NULL ,
  -- Additional fields can be added ...
  PRIMARY KEY (`id`) ,
  UNIQUE INDEX `uniq_name` (`name` ASC));

-- -----------------------------------------------------
-- Table `users_user_roles`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `users_user_roles` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `user_id` INT(11) UNSIGNED NOT NULL ,
  `user_role_id` INT(11) UNSIGNED NOT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_users_user_roles_user_id` (`user_id` ASC) ,
  INDEX `fk_users_user_roles_user_role_id` (`user_role_id` ASC) ,
  CONSTRAINT `fk_users_user_roles_user_id`
    FOREIGN KEY (`user_id`)
    REFERENCES `users` (`id`)
    ON DELETE CASCADE
    ON UPDATE NO ACTION,
  CONSTRAINT `fk_users_user_roles_user_role_id`
    FOREIGN KEY (`user_role_id`)
    REFERENCES `user_roles` (`id`)
    ON DELETE CASCADE
    ON UPDATE NO ACTION)
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci;

-- -----------------------------------------------------
-- Table `user_logins`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `user_logins` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `ip` VARCHAR(65) NOT NULL ,
  `agent` VARCHAR(65) NOT NULL ,
  `login` VARCHAR(255) NOT NULL ,
  `created` INT(11) UNSIGNED NOT NULL ,
  `user_id` INT(11) DEFAULT NULL ,
  PRIMARY KEY (`id`))
DEFAULT CHARACTER SET = utf8;

-- -----------------------------------------------------
-- Table `user_tokens`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `user_tokens` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `token` VARCHAR(40) NOT NULL ,
  `user_id` INT(11) UNSIGNED NOT NULL ,
  `user_agent` VARCHAR(64) NOT NULL ,
  `created` TIMESTAMP DEFAULT CURRENT_TIMESTAMP NOT NULL ,
  `expires` INT(11) UNSIGNED NOT NULL ,
  PRIMARY KEY (`id`) ,
  UNIQUE INDEX `uniq_token` (`token`) ,
  INDEX `fk_user_tokens_user_id` (`user_id` ASC) ,
  CONSTRAINT `fk_user_tokens_user_id`
    FOREIGN KEY (`user_id`)
    REFERENCES `users` (`id`)
    ON DELETE CASCADE
    ON UPDATE NO ACTION)
DEFAULT CHARACTER SET = utf8
COLLATE = utf8_general_ci;

-- -----------------------------------------------------
-- Table `rules`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `rules` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `type` VARCHAR(255) NOT NULL ,
  `key` VARCHAR(255) NOT NULL ,
  `rule` VARCHAR(255) NOT NULL ,
  PRIMARY KEY (`id`))
DEFAULT CHARACTER SET = utf8;

INSERT INTO `rules` (`type`, `key`,`rule`) VALUES ('controller', 'user_management', 'role:admin');
INSERT INTO `rules` (`type`, `key`,`rule`) VALUES ('controller', 'user_logs', 'role:admin');
INSERT INTO `rules` (`type`, `key`,`rule`) VALUES ('controller', 'user_config', 'role:admin');
INSERT INTO `rules` (`type`, `key`,`rule`) VALUES ('controller', 'user_logs', 'role:user');
INSERT INTO `rules` (`type`, `key`,`rule`) VALUES ('controller', 'user_config', 'role:user');

-- -----------------------------------------------------
-- Table `user_config`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `user_config` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `user_id` INT(11) UNSIGNED NOT NULL ,
  `group` VARCHAR(255) NOT NULL ,
  `key` VARCHAR(255) NOT NULL ,
  `value` TEXT NOT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_user_config_user_id` (`user_id` ASC) ,
  CONSTRAINT `fk_user_config_user_id`
    FOREIGN KEY (`user_id`)
    REFERENCES `users` (`id`)
    ON DELETE CASCADE
    ON UPDATE NO ACTION)
DEFAULT CHARACTER SET = utf8;

-- -----------------------------------------------------
-- Table `log_models`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `log_models` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `model` VARCHAR(255) NOT NULL ,
  `model_id` INT(11) UNSIGNED NOT NULL ,
  `user_id` INT(11) UNSIGNED NOT NULL ,
  `method` VARCHAR(255) NOT NULL ,
  `data` TEXT NOT NULL ,
  `created` INT(11) NOT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_log_models_user_id` (`user_id` ASC) ,
  CONSTRAINT `fk_log_models_user_id`
    FOREIGN KEY (`user_id`)
    REFERENCES `users` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
DEFAULT CHARACTER SET = utf8;


-- -----------------------------------------------------
-- Table `log_controllers`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `log_controllers` (
  `id` INT(11) UNSIGNED NOT NULL AUTO_INCREMENT ,
  `controller` VARCHAR(255) NOT NULL ,
  `uri` VARCHAR(255) NOT NULL , -- Assuming uri will not need more than 255 characters.
  `user_id` INT(11) UNSIGNED NOT NULL ,
  `created` INT(11) NOT NULL ,
  PRIMARY KEY (`id`) ,
  INDEX `fk_log_controllers_user_id` (`user_id` ASC) ,
  CONSTRAINT `fk_log_controllers_user_id`
    FOREIGN KEY (`user_id`)
    REFERENCES `users` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
DEFAULT CHARACTER SET = utf8;

-- -----------------------------------------------------
-- Table `sessions`
-- -----------------------------------------------------
CREATE TABLE  `sessions` (
  `session_id` VARCHAR(24) NOT NULL,
  `last_active` INT UNSIGNED NOT NULL,
  `contents` TEXT NOT NULL,
  PRIMARY KEY (`session_id`),
  INDEX (`last_active`))
ENGINE = MYISAM;

The SQL scheme subsumes the SQL schemes for Red, Green, Yellow as well as Blue (see the corresponding repositories). In case the default database connection is not applicable, application/config/database.php and application/config/session.php should be adapted accordingly.

If the installation is not located in the server root (e.g. localhost/kohana-demo), application/bootstrap.php and .htacess need to be updated according to the location of kohana-demo.

The installation should now be ready and can be accessed via localhost/kohana-demo or the corresponding URL as configured in the previous step.

Screenshots

A few screenshots are shown in Figure 1. Authentication as well as user creation is realized through the Red module, similarly controller access logs are created through Yellow, a provided user configuration option with direct impact on the user interface is realized through the Blue module and for administrators, the Green module allows to edit the two available users. CSS and JS files are served through the Media module, the navigation is created through the Navigation module and the edit form for users is generated and validated by the Gaps module. Overall, the application is self-explanatory while demonstrating simple uses cases for all presented modules.

login1 login2 config1 config2
logs user users

Figure 1 (click to enlarge): Screenshots from the demo application demonstrating the login as well as some individual components as described in the text.

What is your opinion on this article? Let me know your thoughts on Twitter @davidstutz92 or LinkedIn in/davidstutz92.