API Docs for: 1.5.0
Show:

File: src/authAgents/SessionAuthAgent.js

/* global define */
define(["structures/CAPIError", "storages/LocalStorage"], function (CAPIError, LocalStorage) {
    "use strict";

    /**
     * Creates an instance of SessionAuthAgent object
     *
     * Auth agent handles low level implementation of authorization workflow.
     * By providing a `login` and a `password` in the `authInfo` object, the
     * auth agent will try to create a session:
     *
     *     var SessionAuthAgent({login: "admin", password: "publish"});
     *
     * The session auth agent is also able to reuse an existing session, to do
     * that it needs to receive an object with the session info:
     *
     *     var new SessionAuthAgent({
     *            name: "eZSESSID",
     *            identifier: "sessionidentifier",
     *            href: "/api/ezp/v2/users/session/sessionidentifier",
     *            csrfToken: "longCsrfToken",
     *        });
     *
     * @class SessionAuthAgent
     * @constructor
     * @param authInfo {Object} object literal containg the credentials (`login`
     * and `password`) or the session info of an already existing one (`name`,
     * `identifier`, `href` and `csrfToken`)
     * @param authInfo.login {String} user login
     * @param authInfo.password {String} user password
     * @param authInfo.name {String} name of the session
     * @param authInfo.identifier {String} identifier of the session
     * @param authInfo.href {String} refresh resource URI for the session
     * @param authInfo.csrfToken {String} CSRF Token
     * @param [storage=LocalStorage] {StorageAbstraction} storage to be used. By default a LocalStorage will be utilized
     */
    var SessionAuthAgent = function (authInfo, storage) {
            /**
             * The CAPI instance. It is set by the call to setCAPI() done while
             * instantiating the CAPI.
             *
             * @property _CAPI
             * @type CAPI
             * @protected
             */
            this._CAPI = null;

            /**
             * The login
             *
             * @property _login
             * @type {String}
             * @default ""
             * @protected
             */
            this._login = '';

            /**
             * The password
             *
             * @property _password
             * @type {String}
             * @default ""
             * @protected
             */
            this._password = '';

            /**
             * The storage to use to store the session info.
             *
             * @property _storage
             * @type {StorageAbstraction}
             * @default LocalStorage
             * @protected
             */
            this._storage = storage || new LocalStorage();

            if ( authInfo ) {
                if ( authInfo.login && authInfo.password ) {
                    this.setCredentials(authInfo);
                } else if ( authInfo.csrfToken && authInfo.identifier && authInfo.name && authInfo.href ) {
                    this._storeSessionInfo(authInfo);
                } else {
                    throw new CAPIError("Invalid authInfo parameter");
                }
            }
        },
        SAFE_METHODS = {'GET': 1, 'HEAD': 1, 'OPTIONS': 1, 'TRACE': 1};

    /**
     * Constant to be used as storage key for the sessionName
     *
     * @static
     * @const
     * @type {string}
     */
    SessionAuthAgent.KEY_SESSION_NAME = 'ezpRestClient.sessionName';

    /**
     * Constant to be used as storage key for the sessionId
     *
     * @static
     * @const
     * @type {string}
     */
    SessionAuthAgent.KEY_SESSION_ID = 'ezpRestClient.sessionId';

    /**
     * Constant to be used as storage key for the sessionHref
     *
     * @static
     * @const
     * @type {string}
     */
    SessionAuthAgent.KEY_SESSION_HREF = 'ezpRestClient.sessionHref';

    /**
     * Constant to be used as storage key for the csrfToken
     *
     * @static
     * @const
     * @type {string}
     */
    SessionAuthAgent.KEY_CSRF_TOKEN = 'ezpRestClient.csrfToken';

    /**
     * Checks that the current user is still logged in. To be considered as
     * logged in, the storage should have a session id and the refresh calls
     * should be successful.
     * If the storage does not contain any session info, the callback is called
     * with `true` as its first argument, otherwise, the callback is called
     * with the `error` and `result` from {{#crossLink
     * "UserService/refreshSession:method"}}UserService.refreshSession{{/crossLink}}.
     *
     * @param {Function} callback
     * @method isLoggedIn
     */
    SessionAuthAgent.prototype.isLoggedIn = function (callback) {
        var that = this,
            userService = this._CAPI.getUserService(),
            sessionId = this._storage.getItem(SessionAuthAgent.KEY_SESSION_ID);

        if ( sessionId === null) {
            callback(true, false);
            return;
        }
        userService.refreshSession(sessionId, function (error, result) {
            if ( error ) {
                that._resetStorage();
            }
            callback(error, result);
        });
    };

    /**
     * Called every time a new request cycle is started,
     * to ensure those requests are correctly authenticated.
     *
     * A cycle may contain one or more queued up requests
     *
     * @method ensureAuthentication
     * @param done {Function} Callback function, which is to be called by the implementation to signal the authentication has been completed.
     */
    SessionAuthAgent.prototype.ensureAuthentication = function (done) {
        if (this._storage.getItem(SessionAuthAgent.KEY_SESSION_ID) !== null) {
            done(false, true);
            return;
        }

        var that = this,
            userService = this._CAPI.getUserService(),
            sessionCreateStruct = userService.newSessionCreateStruct(
                this._login,
                this._password
            );

        userService.createSession(
            sessionCreateStruct,
            function (error, sessionResponse) {
                var session;

                if (error) {
                    done(error, sessionResponse);
                    return;
                }

                session = sessionResponse.document.Session;
                that._storeSessionInfo({
                    name: session.name,
                    href: session._href,
                    identifier: session.identifier,
                    csrfToken: session.csrfToken,
                });

                done(false, sessionResponse);
            }
        );
    };

    /**
     * Tries to log in in the REST API. If the storage already contains a
     * session id, first it tries to log out before doing the log in.
     *
     * @method logIn
     * @param {Function} callback
     */
    SessionAuthAgent.prototype.logIn = function (callback) {
        var that = this;

        if ( this._storage.getItem(SessionAuthAgent.KEY_SESSION_ID) !== null ) {
            this.logOut(function (error, result) {
                that.ensureAuthentication(callback);
            });
        } else {
            this.ensureAuthentication(callback);
        }
    };

    /**
     * Hook to allow the modification of any request, for authentication purposes, before
     * sending it out to the backend
     *
     * @method authenticateRequest
     * @param request {Request}
     * @param done {Function}
     */
    SessionAuthAgent.prototype.authenticateRequest = function (request, done) {
        var token = this._storage.getItem(SessionAuthAgent.KEY_CSRF_TOKEN);

        if ( SAFE_METHODS[request.method.toUpperCase()] !== 1 && token !== null ) {
            request.headers["X-CSRF-Token"] = token;
        }

        done(false, request);
    };

    /**
     * Log out. If the client did not logged in yet, the callback is called with
     * `false` and `true` as arguments, otherwise the callback is called with the
     * `error` and the `result` from {{#crossLink
     * "UserService/deleteSession:method"}}userService.deleteSession{{/crossLink}}.
     *
     * @method logOut
     * @param done {Function}
     */
    SessionAuthAgent.prototype.logOut = function (done) {
        var userService = this._CAPI.getUserService(),
            sessionHref = this._storage.getItem(SessionAuthAgent.KEY_SESSION_HREF),
            that = this;

        if ( sessionHref === null ) {
            done(false, true);
            return;
        }

        userService.deleteSession(
            sessionHref,
            function (error, response) {
                if ( !error ) {
                    that._resetStorage();
                }
                done(error, response);
            }
        );
    };

    /**
     * Set the instance of the CAPI to be used by the agent
     *
     * @method setCAPI
     * @param CAPI {CAPI} current instance of the CAPI object
     */
    SessionAuthAgent.prototype.setCAPI = function (CAPI) {
        this._CAPI = CAPI;
    };

    /**
     * Set the credentials
     *
     * @method setCredentials
     * @param {Object} credentials
     * @param {String} credentials.login
     * @param {String} credentials.password
     */
    SessionAuthAgent.prototype.setCredentials = function (credentials) {
        this._login = credentials.login;
        this._password = credentials.password;
    };

    /**
     * Resets the storage associated with this auth agent
     *
     * @method _resetStorage
     * @protected
     */
    SessionAuthAgent.prototype._resetStorage = function () {
        this._storage.removeItem(SessionAuthAgent.KEY_SESSION_NAME);
        this._storage.removeItem(SessionAuthAgent.KEY_SESSION_ID);
        this._storage.removeItem(SessionAuthAgent.KEY_SESSION_HREF);
        this._storage.removeItem(SessionAuthAgent.KEY_CSRF_TOKEN);
    };

    /**
     * Stores the session information in the storage
     *
     * @method _storeSessionInfo
     * @param {Object} session an object describing the session
     * @param session.name {String} the name of the session
     * @param session.identifier {String} the identifier of the session
     * @param session.href {String} the resource uri to refresh the session
     * @param session.csrfToken {String} the CSRF Token associated with the
     * session
     * @protected
     */
    SessionAuthAgent.prototype._storeSessionInfo = function (session) {
        this._storage.setItem(SessionAuthAgent.KEY_SESSION_NAME, session.name);
        this._storage.setItem(SessionAuthAgent.KEY_SESSION_HREF, session.href);
        this._storage.setItem(SessionAuthAgent.KEY_SESSION_ID, session.identifier);
        this._storage.setItem(SessionAuthAgent.KEY_CSRF_TOKEN, session.csrfToken);
    };

    return SessionAuthAgent;
});