Commit bfd8a21f authored by Rene Saarsoo's avatar Rene Saarsoo
Browse files

Rewrite of authentication logic.

Created Docs.Auth which performs the actual login/logout stuff.
Docs.view.auth.Form represents the form at the header of the app,
firering "login" and "logout" events.

The Auth controller just binds these two things together - listening
to the events from AuthForm, performing the login with Docs.Auth
and calling back to AuthForm to change how it looks after login.
parent f5e65586
Loading
Loading
Loading
Loading
+0 −2
Original line number Diff line number Diff line
@@ -23,8 +23,6 @@ Ext.define('Docs.Application', {
        'Welcome',
        'Failure',
        'Classes',
        'Comments',
        'CommentsMeta',
        'Search',
        'InlineExamples',
        'Examples',

template/app/Auth.js

0 → 100644
+163 −0
Original line number Diff line number Diff line
/**
 * Manages authentication (login/logout) of user.
 */
Ext.define('Docs.Auth', {
    singleton: true,
    requires: [
        'Ext.Ajax',
        'Ext.JSON',
        'Ext.util.Cookies'
    ],

    /**
     * Checks if a user is logged in server side and sets up a local
     * session if they are.
     * @param {Function} callback
     * @param {Object} scope
     */
    init: function(callback, scope) {
        if (!Docs.enableComments) {
            return;
        }

        Ext.Ajax.request({
            url: Docs.baseUrl + '/session_new',
            params: { sid: this.getSid() },
            method: 'GET',
            cors: true,
            callback: function(options, success, response) {
                if (response && response.responseText) {
                    var data = Ext.JSON.decode(response.responseText);

                    if (data && data.sessionID) {
                        this.setSid(data.sessionID);
                    }

                    if (data && data.userName) {
                        this.currentUser = data;
                    }

                    callback && callback.call(scope);
                }
                else {
                    // when comments server is down or something else
                    // is seriously wrong, don't fire the callback.
                    // By this we stop the whole comments system.
                }
            },
            scope: this
        });
    },

    /**
     * Attempts to log a user in.
     *
     * @param {Object} cfg
     * @param {String} cfg.username
     * @param {String} cfg.password
     * @param {Boolean} cfg.remember True if "Remember Me" was checked.
     * @param {Function} cfg.success Function to call when login succeeds.
     * @param {Function} cfg.failure Function to call when login fails.
     * @param {Function} cfg.failure.reason The failure message from backend.
     * @param {Object} cfg.scope Scope for both callbacks.
     */
    login: function(cfg) {
        Ext.Ajax.request({
            url: Docs.baseUrl + '/login',
            method: 'POST',
            cors: true,
            params: {
                username: cfg.username,
                password: cfg.password
            },
            callback: function(options, success, response) {
                var data = Ext.JSON.decode(response.responseText);
                if (data.success) {
                    this.currentUser = data;
                    this.setSid(data.sessionID, cfg.remember);
                    cfg.success && cfg.success.call(cfg.scope);
                }
                else {
                    cfg.failure && cfg.failure.call(cfg.scope, data.reason);
                }
            },
            scope: this
        });
    },

    /**
     * Logs the current user out.
     * @param {Function} callback
     * @param {Object} scope
     */
    logout: function(callback, scope) {
        Ext.Ajax.request({
            url: Docs.baseUrl + '/logout?sid=' + this.getSid(),
            method: 'POST',
            cors: true,
            callback: function() {
                this.currentUser = undefined;
                callback && callback.call(scope);
            },
            scope: this
        });
    },

    /**
     * Sets the session ID.
     * @param {String} sid  The session ID
     * @param {Boolean} remember  'Remember me' flag is set
     * @private
     */
    setSid: function(sid, remember) {
        this.sid = sid;

        if (sid) {
            var expires = null;
            if (remember) {
                expires = new Date();
                expires.setTime(expires.getTime() + (60 * 60 * 24 * 30 * 1000)); // 30 days
            }
            Ext.util.Cookies.set('sid', sid, expires);
        }
        else {
            Ext.util.Cookies.clear('sid');
        }
    },

    /**
     * Returns the current session ID.
     * @return {String}
     */
    getSid: function() {
        if (!this.sid) {
            this.sid = Ext.util.Cookies.get('sid');
        }
        return this.sid;
    },

    /**
     * Returns the record of currently logged in user.
     * @return {Object}
     */
    getUser: function() {
        return this.currentUser;
    },

    /**
     * True if a user is logged in.
     * @return {Boolean}
     */
    isLoggedIn: function() {
        return !!this.getUser();
    },

    /**
     * True if current user is moderator.
     * @return {Boolean}
     */
    isModerator: function() {
        return this.getUser() && this.getUser().mod;
    }

});
+27 −2
Original line number Diff line number Diff line
@@ -3,12 +3,37 @@
 */
Ext.define('Docs.Comments', {
    singleton: true,
    requires: [
        "Docs.Auth",
        "Ext.data.JsonP",
        "Ext.Ajax"
    ],

    /**
     * @inheritdoc Docs.controller.AuthHelpers#request
     * Performs request to the comments server.
     *
     * Works as if calling Ext.Ajax.request or Ext.data.JsonP.request
     * directly, but prefixes the URL with docs base URL and database
     * name and adds Session ID.
     *
     * @param {String} proxy Should we perform "ajax" or "jsonp" request.
     * @param {Object} config
     */
    request: function(type, config) {
        Docs.App.getController("Comments").request(type, config);
        config.url = this.buildUrl(config.url);
        if (type === "jsonp") {
            Ext.data.JsonP.request(config);
        }
        else {
            // Allow doing Cross Origin request.
            config.cors = true;
            Ext.Ajax.request(config);
        }
    },

    buildUrl: function(url) {
        url = Docs.baseUrl + '/' + Docs.commentsDb + '/' + Docs.commentsVersion + url;
        return url + (url.match(/\?/) ? '&' : '?') + 'sid=' + Docs.Auth.getSid();
    }

});
+31 −171
Original line number Diff line number Diff line
/**
 * Authentication controller.
 *
 * Here we just bind together the Docs.Auth and AuthForm component.
 */
Ext.define('Docs.controller.Auth', {
    extend: 'Ext.app.Controller',

    requires: [
        'Ext.util.Cookies',
        'Docs.Tip'
        'Docs.Auth'
    ],

    refs: [
        {
            ref: 'auth',
            selector: 'authentication'
            ref: 'authForm',
            selector: 'authForm'
        }
    ],

    init: function() {
        this.sid = Ext.util.Cookies.get('sid');
        this.currentUser = {};

        this.addEvents(
            /**
             * @event loggedIn
             * Fired after user logs in
             */
            "loggedIn",

            /**
             * @event loggedOut
             * Fired after user logs out
             */
            "loggedOut",

            /**
             * @event available
             * Fired if the authorisation is available
             */
            "available"
        );

        if (!Docs.enableComments) {
            return;
        }

        this.control({
            'authentication': {
                afterrender: function(cmp) {
                    cmp.el.addListener('click', function(e, el) {
                        cmp.showLoginForm();
                    }, this, {
                        preventDefault: true,
                        delegate: '.login'
                    });

                    cmp.el.addListener('click', function(e, el) {
                        this.logout();
                    }, this, {
                        preventDefault: true,
                        delegate: '.logout'
                    });

                    this.retrieveSession();
                }
            'authForm': {
                afterrender: this.initSession,
                login: this.login,
                logout: this.logout
            }
        });
    },

    /**
     * Checks if a user is logged in server side and sets up a local
     * session if they are.
     * @private
     */
    retrieveSession: function() {
        Ext.Ajax.request({
            url: Docs.baseUrl + '/session_new',
            params: { sid: this.sid },
            method: 'GET',
            cors: true,
            callback: function(options, success, response) {
                if (response && response.responseText) {
                    var data = Ext.JSON.decode(response.responseText);

                    if (data && data.sessionID) {
                        this.setSid(data.sessionID);
                    }

                    if (data && data.userName) {
                        this.currentUser = data;
    initSession: function() {
        Docs.Auth.init(function() {
            if (Docs.Auth.isLoggedIn()) {
                this.setLoggedIn();
                    } else {
                        this.setLoggedOut();
            }

                    this.fireEvent('available');
            else {
                this.setLoggedOut();
            }
            },
            scope: this
        });
        }, this);
    },

    /**
     * Authenticates a user
     * @param {String} username
     * @param {String} password
     * @param {Boolean} remember True if "Remember Me" was checked.
     * @param {Ext.Element} tipTarget Target where to anchor login failure messages.
     */
    login: function(username, password, remember, tipTarget) {
        Ext.Ajax.request({
            url: Docs.baseUrl + '/login',
            method: 'POST',
            cors: true,
            params: {
    login: function(username, password, remember) {
        Docs.Auth.login({
            username: username,
                password: password
            },
            callback: function(options, success, response) {
                var data = Ext.JSON.decode(response.responseText);
                if (data.success) {
                    this.currentUser = data;
                    this.setSid(data.sessionID, { remember: remember });
                    this.setLoggedIn();
                } else {
                    Docs.Tip.show(data.reason, tipTarget, 'bottom');
                }
            password: password,
            remember: remember,
            success: this.setLoggedIn,
            failure: function(reason) {
                this.getAuthForm().showMessage(reason);
            },
            scope: this
        });
    },

    /**
     * Logs out a user
     * @private
     */
    logout: function() {
        Ext.Ajax.request({
            url: Docs.baseUrl + '/logout?sid=' + this.sid,
            method: 'POST',
            cors: true,
            callback: function(){
                this.setLoggedOut();
            },
            scope: this
        });
        Docs.Auth.logout(this.setLoggedOut, this);
    },

    /**
     * Marks the user as logged in.
     * @private
     */
    setLoggedIn: function() {
        if (this.currentUser) {
            this.getAuth().showLoggedIn(this.currentUser.userName);
            this.fireEvent('loggedIn');
        }
        this.getAuthForm().showLoggedIn(Docs.Auth.getUser().userName);
        this.getController("Tabs").showCommentsTab();
    },

    /**
     * Marks a user as logged out.
     * @private
     */
    setLoggedOut: function(user) {
        this.currentUser = {};
        this.getAuth().showLoggedOut();
        this.fireEvent('loggedOut');
    },

    /**
     * Checks if a user is logged in.
     * @return {Boolean} true if the user is logged in
     */
    isLoggedIn: function() {
        return !!this.currentUser.userName;
    },

    /**
     * True when current user is moderator.
     * @return {Boolean}
     */
    isModerator: function() {
        return this.currentUser.mod;
    },

    /**
     * Sets the session ID.
     * @param {String} sid  The session ID
     * @param {Object} opts (optional)
     * @param {Boolean} opts.remember  'Remember me' flag is set
     */
    setSid: function(sid, opts) {

        this.sid = sid;

        if (sid) {
            var expires = null;
            if (opts && opts.remember) {
                expires = new Date();
                expires.setTime(expires.getTime() + (60 * 60 * 24 * 30 * 1000)); // 30 days
            }
            Ext.util.Cookies.set('sid', sid, expires);
        } else {
            Ext.util.Cookies.clear('sid');
        }
        this.getAuthForm().showLoggedOut();
        this.getController("Tabs").hideCommentsTab();
    }

});
+1 −1
Original line number Diff line number Diff line
@@ -11,7 +11,7 @@ Ext.define('Docs.view.Comments', {
        // and indeed, when running in browser, the app will work just
        // fine, but when doing e.g. "rake gem" something goes wrong
        // and the "sencha create jsb" command just hangs.
        'Docs.view.auth.Login',
        'Docs.view.auth.Form',
        'Docs.view.comments.Form'
    ],

Loading