Skip to main content

Overview

Sistema Magdaleno implements a comprehensive authentication and authorization system using CakePHP’s Auth Component combined with Access Control Lists (ACL). This provides fine-grained permission control over all controllers and actions.

Authentication Architecture

Components Used

  1. Auth Component - Handles user login, session management, and basic authorization
  2. Acl Component - Manages permission trees and access control
  3. Session Component - Maintains user state across requests

Configuration

Location: app/app_controller.php
class AppController extends Controller {
    var $components = array(
        'Acl',      // ACL permission checking
        'Auth',     // Authentication & authorization
        'Session'   // Session management
    );
}

Authentication Flow

Login Configuration

function beforeFilter() {
    parent::beforeFilter();
    
    // Login page location
    $this->Auth->loginAction = array(
        'controller' => 'users', 
        'action' => 'login'
    );
    
    // Where to redirect after successful login
    $this->Auth->loginRedirect = array(
        'controller' => 'users', 
        'action' => 'home'
    );
    
    // Custom login error message
    $this->Auth->loginError = 'El Usuario o el Password no son válidos por favor intenta nuevamente';
    
    // Use controller-based authorization
    $this->Auth->authorize = 'controller';
}

Public Access

Some actions are accessible without authentication:
// Allow public access to these actions
$this->Auth->allow(
    'add_vendedor',      // Vendor registration
    'consulta_codigo',   // Code lookup
    'registrado',        // Registration confirmation
    'display'            // Static pages
);

Authorization Check

function isAuthorized() {
    // Return true to delegate to ACL system
    // Return false to deny access
    return true;
}

Access Control Lists (ACL)

ACL Architecture

The ACL system uses a dual-tree structure to manage permissions:
  1. ACOs (Access Control Objects) - What can be controlled (resources)
  2. AROs (Access Request Objects) - Who requests access (users/groups)

ACL Database Tables

acos (Access Control Objects)

Defines the hierarchy of controllable resources. Structure:
controllers (root)
├── Pages
│   ├── display
│   ├── add
│   ├── edit
│   ├── index
│   ├── view
│   └── delete
├── Users
│   ├── login
│   ├── logout
│   ├── index
│   ├── add
│   ├── edit
│   └── delete
├── Galleries
│   ├── index
│   ├── view
│   ├── add
│   ├── edit
│   └── delete
└── [Other Controllers]
Model: app/models/aco.php
class Aco extends AppModel {
    var $name = 'Aco';
    var $actsAs = array('Tree');  // Nested set behavior
    var $uses = 'acos';
    
    var $belongsTo = array(
        'ParentAco' => array(
            'className' => 'Aco',
            'foreignKey' => 'parent_id'
        )
    );
    
    var $hasMany = array(
        'ChildAco' => array(
            'className' => 'Aco',
            'foreignKey' => 'parent_id'
        )
    );
    
    var $hasAndBelongsToMany = array(
        'Aro' => array(
            'joinTable' => 'aros_acos',
            'foreignKey' => 'aco_id',
            'associationForeignKey' => 'aro_id'
        )
    );
}

aros (Access Request Objects)

Defines the hierarchy of requesters (users and groups). Structure:
Group: administradores (id=1)
├── User: jose (id=2)
├── User: yuliet (id=3)
├── User: emily (id=4)
└── User: admin (id=5)
Model: app/models/aro.php
class Aro extends AppModel {
    var $name = 'Aro';
    
    var $belongsTo = array(
        'ParentAro' => array(
            'className' => 'Aro',
            'foreignKey' => 'parent_id'
        )
    );
    
    var $hasMany = array(
        'ChildAro' => array(
            'className' => 'Aro',
            'foreignKey' => 'parent_id'
        )
    );
    
    var $hasAndBelongsToMany = array(
        'Aco' => array(
            'joinTable' => 'aros_acos',
            'foreignKey' => 'aro_id',
            'associationForeignKey' => 'aco_id'
        )
    );
}

aros_acos (Permission Junction)

Links AROs to ACOs with specific CRUD permissions. Columns:
  • aro_id - Reference to ARO (user/group)
  • aco_id - Reference to ACO (controller/action)
  • _create - Create permission
  • _read - Read permission
  • _update - Update permission
  • _delete - Delete permission
Permission Values:
  • 1 = Allow - Explicitly grant permission
  • 0 = Deny - Explicitly deny permission
  • -1 = Inherit - Inherit from parent node
Model: app/models/aros_aco.php
class ArosAco extends AppModel {
    var $name = 'ArosAco';
    
    var $belongsTo = array(
        'Aro' => array(
            'foreignKey' => 'aro_id'
        ),
        'Aco' => array(
            'foreignKey' => 'aco_id'
        )
    );
}

User & Group Integration

User Model Integration

File: app/models/user.php
class User extends AppModel {
    var $name = 'User';
    var $primaryKey = 'id_usuario';
    
    // Enable ACL behavior
    var $actsAs = array('Acl' => array('requester'));
    
    var $belongsTo = array(
        'Group' => array(
            'foreignKey' => 'groups_idgrupos'
        )
    );
    
    // Define parent node for ACL tree
    function parentNode() {
        if (!$this->id && empty($this->data)) {
            return null;
        }
        
        $data = $this->data;
        if (empty($this->data)) {
            $data = $this->read();
        }
        
        if (!$data['User']['groups_idgrupos']) {
            return null;
        } else {
            // User belongs under their group
            return array(
                'Group' => array(
                    'idgrupos' => $data['User']['groups_idgrupos']
                )
            );
        }
    }
}

Group Model Integration

File: app/models/group.php
class Group extends AppModel {
    var $name = 'Group';
    var $primaryKey = 'idgrupos';
    
    // Enable ACL behavior
    var $actsAs = array('Acl' => array('requester'));
    
    // Groups are root nodes in ARO tree
    function parentNode() {
        return null;
    }
}

ACL Tree Generation

Automatic ACL Building

The build_acl() method automatically generates ACO tree from controllers: Location: app/app_controller.php::build_acl() Process:
  1. Create root ACO node: controllers
  2. Scan all controllers in app/controllers/
  3. For each controller:
    • Create controller ACO node
    • Scan public methods
    • Create action ACO nodes for each method
  4. Support plugin controllers
Usage:
// Only works in debug mode
Configure::write('debug', 2);

// Access via browser
/pages/build_acl
Key Functions:
// Get class methods for a controller
function _getClassMethods($ctrlName = null)

// Check if controller is a plugin
function _isPlugin($ctrlName = null)

// Get plugin name from controller path
function _getPluginName($ctrlName = null)

// Get plugin controller names
function _getPluginControllerNames()

Permission Management

Setting Permissions

Permissions are managed through the aros_acos junction table. Grant Full Access to Group:
// Grant 'administradores' group full access to all controllers
$this->Acl->allow(
    array('Group' => array('idgrupos' => 1)), // ARO
    'controllers',                             // ACO
    '*'                                        // All permissions
);
Grant Specific Action Access:
// Allow group to add galleries
$this->Acl->allow(
    array('Group' => array('idgrupos' => 1)),
    'controllers/Galleries/add',
    'create'
);
Deny Access:
// Deny delete access to videos
$this->Acl->deny(
    array('Group' => array('idgrupos' => 2)),
    'controllers/Videos/delete',
    'delete'
);

Checking Permissions

// Check if current user has permission
if ($this->Acl->check(
    array('User' => array('id_usuario' => $this->Auth->user('id_usuario'))),
    'controllers/Galleries/edit',
    'update'
)) {
    // User has permission
}

Password Security

Password Hashing

Passwords are hashed using SHA1 before storage:
// In User model or controller
$this->data['User']['password'] = Security::hash(
    $this->data['User']['password'], 
    'sha1', 
    true
);
Example from database:
SELECT username, password FROM users;
-- jose | 27fd5f05772c81f3d7a067762634b28f07ce8ba8
-- admin | a22aee241f2699b81c976f9effd8afd7cd61e6d4

Password Validation

File: app/models/user.php
var $validate = array(
    'password' => array(
        'rule' => array('minLength', 4),
        'message' => 'Password must be at least 4 characters'
    ),
    'newpass' => array(
        'rule' => array('minLength', 4),
        'message' => 'New password must be at least 4 characters'
    ),
    'newpassconfirm' => array(
        'rule' => array('minLength', 4),
        'message' => 'Confirmation must match new password'
    )
);

User Roles & Groups

Default Group

Table: groups
IDNameTitle
1administradoresadmin

User Assignment

Users are assigned to groups via groups_idgrupos foreign key:
// Create user in admin group
$this->data['User']['groups_idgrupos'] = 1;
$this->User->save($this->data);

Permission Inheritance

Users inherit permissions from their group:
Group: administradores (full access)
└── User: jose
    └── Inherits all admin permissions

Session Management

User Session Data

After successful login, user data is stored in session:
// Get current user ID
$userId = $this->Auth->user('id_usuario');

// Get current username
$username = $this->Auth->user('username');

// Get all user data
$userData = $this->Auth->user();

Login/Logout

Login:
// UsersController::login()
if ($this->Auth->login()) {
    // Redirect to loginRedirect
    $this->redirect($this->Auth->redirect());
}
Logout:
// UsersController::logout()
$this->redirect($this->Auth->logout());

Security Best Practices

1. Always Hash Passwords

// GOOD
$password = Security::hash($plainPassword, 'sha1', true);

// BAD
$password = $plainPassword; // Never store plain text!

2. Use ACL for Authorization

// GOOD - Check ACL permissions
$this->Auth->authorize = 'controller';

// Then implement isAuthorized() with ACL checks

3. Restrict Sensitive Actions

// Only allow public registration, keep admin functions protected
$this->Auth->allow('register', 'forgot_password');

4. Validate User Input

var $validate = array(
    'username' => array(
        'rule' => 'alphaNumeric',
        'message' => 'Only letters and numbers allowed'
    )
);

5. Use HTTPS for Login

// Force SSL for login page
if (!$this->RequestHandler->isSSL()) {
    $this->redirect('https://' . env('SERVER_NAME') . $this->here);
}

Permission Scenarios

Scenario 1: Full Admin Access

// Grant admin group full access to everything
$this->Acl->allow(
    array('Group' => array('idgrupos' => 1)),
    'controllers',
    array('create', 'read', 'update', 'delete')
);

Scenario 2: Read-Only User

// Grant read-only access to galleries
$this->Acl->allow(
    array('User' => array('id_usuario' => 5)),
    'controllers/Galleries',
    'read'
);

$this->Acl->deny(
    array('User' => array('id_usuario' => 5)),
    'controllers/Galleries',
    array('create', 'update', 'delete')
);

Scenario 3: Limited Editor

// Allow editor to create/edit galleries but not delete
$this->Acl->allow(
    array('Group' => array('idgrupos' => 2)),
    'controllers/Galleries',
    array('create', 'read', 'update')
);

$this->Acl->deny(
    array('Group' => array('idgrupos' => 2)),
    'controllers/Galleries/delete',
    'delete'
);

Troubleshooting

Common Issues

1. ACL tables not populated
// Run build_acl() to generate ACO tree
/pages/build_acl
2. Users not in ARO tree
// Ensure User model has actsAs Acl behavior
var $actsAs = array('Acl' => array('requester'));

// Save user again to trigger ARO creation
$this->User->save($userData);
3. Permission denied unexpectedly
// Check ARO/ACO link exists in aros_acos
// Check permission values (1=allow, 0=deny, -1=inherit)

Build docs developers (and LLMs) love