Concierge

Concierge is an extensible service layer for your application's operational resources -- the services and data stores that support what your application does without being its main purpose. It orchestrates whatever components you configure into one reliable, structured API, so you can focus on what your application does instead of the plumbing that makes it possible.

Out of the box, Concierge provides a complete identity core -- authentication, sessions, and user records -- covering who your users are, what they're allowed to do, and what context follows them through a session. The same component pattern that powers those three extends to any other resource your application needs to manage: an added component gets the same setup, storage, and access conventions as the built-in three, and Concierge doesn't need to know or care what it does.

Synopsis

use Concierge::Desk::Setup;
use Concierge;

# One-time desk setup
Concierge::Desk::Setup::build_quick_desk(
    './desk',
    ['role', 'theme'],       # application-specific user fields
);

# Runtime
my $desk = Concierge->open_desk('./desk');
my $concierge = $desk->{concierge};

# Register and log in a user
$concierge->add_user({
    user_id  => 'alice',
    moniker  => 'Alice',
    email    => 'alice@example.com',
    password => 'secret123',
    role     => 'admin',
});

my $login = $concierge->login_user({
    user_id  => 'alice',
    password => 'secret123',
});

my $user = $login->{user};  # Concierge::Desk::User object
say $user->moniker;         # "Alice"
say $user->session_id;      # random hex token

Concepts

Concierge is built around four ideas: it is extensible, it behaves as a service layer, it orchestrates rather than reimplements, and it exists to simplify an application's operational resources. See perldoc Concierge (CONCEPTS section) for the full discussion; summarized:

How It Works

Desks

A desk is a directory containing the configuration and data files for the three identity-core components -- and, if you've added any, for those too. You create one with Concierge::Desk::Setup, then open it at runtime with Concierge->open_desk(). Opening a desk instantiates all components from the saved configuration and runs session cleanup automatically.

# One-time setup (run once, not on every request)
use Concierge::Desk::Setup;
Concierge::Desk::Setup::build_desk({
    base_dir => './desk',
    auth     => { backend => 'pwd' },
    sessions => { backend => 'database' },
    users    => {
        backend    => 'database',
        app_fields => ['department', 'theme'],
    },
});

# Every request
use Concierge;
my $result    = Concierge->open_desk('./desk');
my $concierge = $result->{concierge};

User Participation Levels

Concierge provides three graduated levels, each returning a Concierge::Desk::User object with methods appropriate to that level:

| Level | Method | User key | Session | User record | Auth | |---|---|---|---|---|---| | Visitor | admit_visitor() | Yes | No | No | No | | Guest | checkin_guest() | Yes | Yes | No | No | | Logged-in | login_user() | Yes | Yes | Yes | Yes |

A guest can be promoted to a logged-in user with login_guest(), which transfers any session data (shopping cart, preferences, etc.) to the new authenticated session.

Between requests, users are restored by user_key (typically stored in a cookie): restore_user($user_key) rehydrates the correct object type with the right data and backend access.

Components

Concierge ships with a complete identity core out of the box, and the same component pattern that powers it extends to anything else your application needs to manage.

Identity Core (built in)

Authentication — Concierge::Auth

Sessions — Concierge::Sessions

User Records — Concierge::Users

See the Concierge::Users README (Field Customization) for the full list of standard fields.

Applications extend this with app_fields at setup time:

Concierge::Desk::Setup::build_desk({
    base_dir => './desk',
    auth     => { backend => 'pwd' },
    sessions => { backend => 'database' },
    users    => {
        backend    => 'database',
        app_fields => [
            { field_name => 'department', type => 'text' },
            { field_name => 'plan',       type => 'enum',
              options => ['free', 'pro', 'enterprise'] },
        ],
    },
});

Field definitions can also override built-in defaults (labels, null values, required flags, etc.) via field_overrides.

All or selected standard fields may also be omitted entirely, except for the required fields and automatic date fields.

Extensibility (bring your own)

Each identity core component can itself be replaced with a conforming alternative -- any drop-in that implements the same method contract (see EXTENSIBILITY in perldoc Concierge) works in place of the built-in Auth, Sessions, or Users component.

Beyond the identity core, a desk can carry any number of additional components -- Organizations, Assets, Guides, Catalog, or anything else your application manages the same way. A component only needs to satisfy the duck-typed contract in Concierge::Desk::Component (a new/setup constructor lifecycle and the { success => ..., message => ... } return convention), wired up via a components block in build_desk():

Concierge::Desk::Setup::build_desk({
    base_dir => './desk',
    auth     => { backend => 'pwd' },
    sessions => { backend => 'database' },
    users    => { backend => 'database' },
    components => {
        organizations => {
            class    => 'Concierge::Organizations',
            optional => 0,
        },
    },
});

Once the desk is open, an added component is reached the same way as the identity core -- through its own accessor on the concierge object ($concierge->organizations) -- but Concierge itself never intervenes in how the component works; that's for the application as it uses it.

See the EXTENSIBILITY section in perldoc Concierge for the full method contracts and patterns for both substitution and extension, including deferred (defer) component initialization.

Consistent Return Values

All Concierge methods return a hashref:

# Success
{ success => 1, message => '...', ... }

# Failure
{ success => 0, message => 'error description' }

Methods never die or croak during normal operation (the one exception is open_desk(), which croaks if the desk directory does not exist, or if a non-optional added component -- see Extensibility above -- fails to load). This makes Concierge safe to use in event-loop and persistent-process environments.

Installation

Installing Concierge from CPAN automatically installs the three component distributions as dependencies:

cpanm Concierge

Or manually:

perl Makefile.PL
make
make test
make install

Requires Perl 5.36 or later.

Documentation

perldoc Concierge          # orchestration API, lifecycle methods, extensibility
perldoc Concierge::Desk::Setup   # desk creation and configuration
perldoc Concierge::Desk::User    # user object methods
perldoc Concierge::Desk::Component  # contract for additional components
perldoc Concierge::Auth    # authentication and token generation
perldoc Concierge::Sessions  # session lifecycle and backends
perldoc Concierge::Users   # user records, field schema, backends

Status

Under active development (v0.13.0). API may change before 1.0.

Author

Bruce Van Allen bva@cruzio.com

License

Artistic License 2.0