Revision 85f1cd1e

b/snf-cyclades-app/synnefo/app_settings/default/ui.py
45 45
# Email from which the feedback emails will be sent from
46 46
FEEDBACK_EMAIL_FROM = DEFAULT_FROM_EMAIL
47 47

  
48
# URL to redirect user to when he logs out from the ui (if not set
49
# settings.LOGIN_URL will be used)
50
#LOGOUT_URL = ""
48
# URL to redirect not authenticated users
49
UI_LOGIN_URL = "/im/login"
50

  
51
# URL to redirect user to when he logs out from the ui
52
UI_LOGOUT_URL = "/im/logout"
51 53

  
52 54
# Flavor options that we provide to the user as predefined
53 55
# cpu/ram/disk combinations on vm create wizard
b/snf-cyclades-app/synnefo/app_settings/urls.py
37 37
    (r'^ui/', include('synnefo.ui.urls')),
38 38
    url(r'^machines/console$', 'synnefo.ui.views.machines_console', name='machines-console'),
39 39
    url(r'^machines/connect$', 'synnefo.ui.views.machines_connect', name='machines-connect'),
40
    (r'^admin/', include('synnefo.admin.urls')),
41 40
    (r'^api/', include('synnefo.api.urls')),
42 41
    (r'^plankton/', include('synnefo.plankton.urls')),
43 42
)
b/snf-cyclades-app/synnefo/ui/static/snf/js/auth.js
1
// Copyright 2012 GRNET S.A. All rights reserved.
2
// 
3
// Redistribution and use in source and binary forms, with or
4
// without modification, are permitted provided that the following
5
// conditions are met:
6
// 
7
//   1. Redistributions of source code must retain the above
8
//      copyright notice, this list of conditions and the following
9
//      disclaimer.
10
// 
11
//   2. Redistributions in binary form must reproduce the above
12
//      copyright notice, this list of conditions and the following
13
//      disclaimer in the documentation and/or other materials
14
//      provided with the distribution.
15
// 
16
// THIS SOFTWARE IS PROVIDED BY GRNET S.A. ``AS IS'' AND ANY EXPRESS
17
// OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
18
// WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19
// PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL GRNET S.A OR
20
// CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
23
// USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
24
// AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
25
// LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
26
// ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27
// POSSIBILITY OF SUCH DAMAGE.
28
// 
29
// The views and conclusions contained in the software and
30
// documentation are those of the authors and should not be
31
// interpreted as representing official policies, either expressed
32
// or implied, of GRNET S.A.
33
// 
34

  
35
;(function(root){
36
    
37
    // Astakos client javascript lib
38
    // Requires jquery and jquery.cookie javascript libs
39
    //
40
    // Usage
41
    // -----
42
    // <script src="jquery.js"></script>
43
    // <script src="jquery.cookie.js"></script>
44
    // <script src="snf/auth.js"></script>
45
    //
46
    //  var astakos_config = {
47
    //        'login_url': '/im/login',
48
    //        'auth_url': '/im/authenticate',
49
    //        'cookie_name': '_pithos2_a',
50
    //        'logout_callback': function(client) {
51
    //            console.log("logging out");
52
    //            client.redirect_to_logout();
53
    //        }
54
    //
55
    //  var astakos_client = new snf.auth.AstakosClient(astakos_config);
56
    //  var user = astakos_client.get_user();
57
    //  if (!user) { astakos_client.redirect_to_login() };
58
    //  console.log(user.username, user.token);
59
    //
60

  
61
    var root = root;
62
    var snf = root.synnefo = root.synnefo || {};
63
    
64
    // init auth namespace
65
    snf.auth = {};
66
    
67
    snf.auth.AstakosClient = function(config) {
68
        this.config = $.extend(this.default_config, config);
69
    }
70

  
71
    snf.auth.AstakosClient.prototype.default_config = {
72
            'logout_url': '/im/logout',
73
            'login_url': '/im/login',
74
            'cookie_name': '_pithos2_a',
75
            'logout_callback': function(client) {
76
                client.redirect_to_logout();
77
            }
78
    }
79

  
80
    snf.auth.AstakosClient.prototype.delete_cookie = function() {
81
        $.cookie(this.config.cookie_name, null);
82
    }
83

  
84
    snf.auth.AstakosClient.prototype.redirect_to_logout = function() {
85
        window.location = this.config.logout_url;
86
    }
87
    
88
    snf.auth.AstakosClient.prototype.redirect_to_login = function() {
89
        window.location = this.config.login_url + "?next=" + window.location.toString();
90
    }
91

  
92
    // delete cookie and redirect to logout
93
    // cookie removal can be forced by passing true as delete_cookie parameter
94
    snf.auth.AstakosClient.prototype.logout = function(delete_cookie) {
95
        var delete_cookie = delete_cookie == undefined ? false : delete_cookie;
96
        if (delete_cookie) {
97
            this.delete_cookie();
98
        }
99
        this.config.logout_callback(this);
100
    }
101

  
102
    snf.auth.AstakosClient.prototype.get_cookie_data = function() {
103
        var data = $.cookie(this.config.cookie_name);
104
        
105
        // remove " characters
106
        if (data) { return data.replace(/\"/g, "") }
107
        return data;
108
    }
109

  
110

  
111
    snf.auth.AstakosClient.prototype.logged_in = function() {
112
        return this.get_cookie_data() == null
113
    }
114

  
115
    // parse cookie data
116
    // astakos sets cookie data using the following pattern: <username>|<token>
117
    snf.auth.AstakosClient.prototype.parse_cookie_data = function(data) {
118
        return {
119
            'username': data.split("|")[0],
120
            'token': data.split("|")[1]
121
        }
122
    }
123
    
124
    // set username and token
125
    snf.auth.AstakosClient.prototype.get_user = function() {
126
        var data = this.get_cookie_data();
127
        if (!data) {
128
            return false;
129
        }
130
        var parsed_data = this.parse_cookie_data(data);
131
        return parsed_data;
132
    }
133
    
134
})(this);
b/snf-cyclades-app/synnefo/ui/static/snf/js/sync.js
180 180
                this.date_send = new Date;
181 181
            }
182 182

  
183
            if (handler_type == "beforeSend") {
184
                arguments[0].setRequestHeader('X-Auth-Token', synnefo.user.token);
185
            }
186

  
183 187
            // error with status code 0 in opera
184 188
            // act as 304 response
185 189
            if (handler_type == "error" && $.browser.opera) {
b/snf-cyclades-app/synnefo/ui/static/snf/js/ui/web/ui_main_view.js
61 61

  
62 62
        beforeOpen: function() {
63 63
            var cont = this.$(".copy-content p");
64
            var token = $.cookie("X-Auth-Token");
64
            var token = snf.user.token;
65 65

  
66 66
            cont.html("");
67 67
            cont.text(token);
......
78 78

  
79 79
        onClose: function() {
80 80
            var cont = this.$(".copy-content p");
81
            var token = $.cookie("X-Auth-Token");
81
            var token = snf.user.token;
82 82
            cont.html("");
83 83
        }
84 84
    });
......
496 496
            var args = util.parse_api_error.apply(util, arguments);
497 497
            
498 498
            // force logout if UNAUTHORIZED request arrives
499
            if (args.code == 401) { snf.ui.logout(); return };
499
            if (args.code == 401) { snf.auth_client.logout(); return };
500 500

  
501 501
            var error_entry = [args.ns, args.code, args.message, args.type, args.details, args];
502 502
            this.error_view.show_error.apply(this.error_view, error_entry);
......
640 640
            synnefo.ui.bind("error", _.bind(this.handle_ui_error, this));
641 641

  
642 642
            this.feedback_view = new views.FeedbackView();
643
            this.invitations_view = new views.InvitationsView();
644 643
            this.public_keys_view = new views.PublicKeysOverlay();
645 644
            
646 645
            if (synnefo.config.use_glance) {
......
685 684
        },
686 685

  
687 686
        init_menu: function() {
688
            $(".usermenu .invitations").click(_.bind(function(e){
689
                e.preventDefault();
690
                this.invitations_view.show();
691
            }, this));
692 687
            $(".usermenu .feedback").click(_.bind(function(e){
693 688
                e.preventDefault();
694 689
                this.feedback_view.show();
......
715 710
          
716 711
          bb.history.start();
717 712

  
718
          this.trigger("initial");
713
          this.trigger("ready");
719 714
        },
720 715

  
721 716
        show_vm_details: function(vm) {
......
957 952

  
958 953
    snf.ui.main = new views.MainView();
959 954
    
960
    snf.ui.logout = function() {
961
        $.cookie("X-Auth-Token", null);
962
        if (snf.config.logout_url !== undefined)
963
        {
964
            window.location = snf.config.logout_url;
965
        } else {
966
            window.location.reload();
967
        }
968
    }
969

  
970 955
    snf.ui.init = function() {
971 956
        if (snf.config.handle_window_exceptions) {
972 957
            window.onerror = function(msg, file, line) {
b/snf-cyclades-app/synnefo/ui/templates/home.html
51 51
    <![endif]-->
52 52
    
53 53
    <script src="{{ SYNNEFO_JS_URL }}utils.js"></script>
54
    <script src="{{ SYNNEFO_JS_URL }}auth.js"></script>
54 55
    <script src="{{ SYNNEFO_JS_URL }}sync.js"></script>
55 56
    <script src="{{ SYNNEFO_JS_URL }}models.js"></script>
56 57
    <script src="{{ SYNNEFO_JS_URL }}glance_models.js"></script>
......
65 66
    <script src="{{ SYNNEFO_JS_WEB_URL }}ui_networks_view.js"></script>
66 67
    <script src="{{ SYNNEFO_JS_WEB_URL }}ui_metadata_view.js"></script>
67 68
    <script src="{{ SYNNEFO_JS_WEB_URL }}ui_feedback_view.js"></script>
68
    <script src="{{ SYNNEFO_JS_WEB_URL }}ui_invitations_view.js"></script>
69 69
    <script src="{{ SYNNEFO_JS_WEB_URL }}ui_create_view.js"></script>
70 70
    <script src="{{ SYNNEFO_JS_WEB_URL }}ui_connect_view.js"></script>
71 71
    <script src="{{ SYNNEFO_JS_WEB_URL }}ui_public_keys_view.js"></script>
......
75 75
    <script src="{{ SYNNEFO_JS_WEB_URL }}ui_main_view.js"></script>
76 76

  
77 77
    <!-- the following views require refactor -->
78
    <script src="{{ SYNNEFO_JS_URL }}invitations.js"></script>
79 78
    <script src="{{ SYNNEFO_JS_URL }}synnefo.js"></script>
80 79
    
81 80
    <script>
......
91 90
        var TIMEOUTS_OCCURED = 0;
92 91
        var SKIP_TIMEOUTS = 1;
93 92
        var UPDATE_INTERVAL = {{ update_interval }};
94
        var LOGOUT_REDIRECT = '{{ logout_redirect }}';
95
        var INVITATIONS_URL = "{% url invitations %}";
96
        var INVITATIONS_TITLE = "{% trans "Invite people" %}";
97 93
        var APP_DEBUG = {% if DEBUG %}true{% else %}false{% endif %};
98 94
        var FEEDBACK_URL = "{% url feedback %}";
99 95
        var FEEDBACK_TITLE = "{% trans "Send feedback" %}";
......
190 186
        <div id="header">
191 187
            <div id="user">
192 188
                <div class="usermenu">
193
                    <div class="username">{{ request.user.uniq }}</div>
189
                    <div class="username"></div>
194 190
                    <ul class="useractions">
195
                        <li class="invitations"><a class="action" href="#">{% trans "invite friends..." %}</a></li>
196 191
                        <li class="feedback"><a class="action" href="#">{% trans "send feedback..." %}</a></li>
197 192
                        <li class="api"><a class="action" href="#">{% trans "API access..." %}</a></li>
198 193
                        <li class="public_keys"><a class="action" href="#">{% trans "ssh public keys..." %}</a></li>
......
312 307

  
313 308
            // bind menu actions
314 309
            $(".usermenu .logout").click(function() {
315
                synnefo.ui.logout();
310
                synnefo.auth_client.logout();
316 311
            });
317 312

  
318 313
            $(".usermenu .api").click(function(){
......
422 417
            <span class="reload-app">{% trans "Reload" %}</span>
423 418
        </div>
424 419
    </div>
425
    {% include "partials/invitations.html" %}
426 420
    <div id="feedback-overlay-content" class="hidden overlay-content feedback-form">
427 421
        <div class="description">
428 422
            <p>
......
559 553
            synnefo.config.handle_window_exceptions = {{ handle_window_exceptions }};
560 554
            synnefo.config.ajax_timeout = {{ timeout }};
561 555
            synnefo.config.skip_timeouts = {{ skip_timeouts }};
562
            synnefo.config.invitations_url = "{% url invitations %}";
563 556
            synnefo.config.machines_icons_url = '{{ SYNNEFO_IMAGES_URL }}icons/machines/';
564 557
            synnefo.config.vm_name_template = {{ vm_name_template|safe }};
565 558
            synnefo.config.flavors_disk_templates_info = {{ flavors_disk_templates_info|safe }};
......
568 561
                'compute':  {{ compute_api_url|safe }}, 
569 562
                'glance': {{ glance_api_url|safe }}
570 563
            };
564
            
571 565
            // TODO: configurable userdata urls in models.js
572 566
            synnefo.config.userdata_url = '/ui/userdata';
573
            synnefo.config.logout_url = '{{ logout_redirect }}';
574 567
            synnefo.config.userdata_keys_url = '{% url keys_collection %}';
575 568
            synnefo.config.userdata_keys_limit = {{ userdata_keys_limit }};
569
            
576 570
            // media config
577 571
            synnefo.config.media_url = '{{ UI_MEDIA_URL }}';
578 572
            synnefo.config.js_url = '{{ SYNNEFO_JS_URL }}';
......
582 576
            synnefo.config.machines_icons_url = '{{ SYNNEFO_IMAGES_URL }}icons/machines/';
583 577
            synnefo.config.support_ssh_os_list = {{ support_ssh_os_list|safe }};
584 578
            synnefo.config.os_created_users = {{ os_created_users|safe }};
579
            
580
            synnefo.config.logout_redirect = '{{ logout_redirect }}';
581
            synnefo.config.login_redirect = '{{ login_redirect }}';
582
            synnefo.config.auth_cookie_name = '{{ auth_cookie_name }}';
583
            
584
            synnefo.auth_client = new synnefo.auth.AstakosClient({
585
                login_url: synnefo.config.login_redirect,
586
                logout_url: synnefo.config.logout_redirect,
587
                cookie_name: synnefo.config.auth_cookie_name
588
            });
589

  
585 590
            // user config
586
            synnefo.user = {};
587
            synnefo.user.username = '{{ request.user.uniq }}';
588
            synnefo.user.token = $.cookie("X-Auth-Token");
591
            synnefo.user = synnefo.auth_client.get_user();
592
            if (!synnefo.user) { synnefo.auth_client.redirect_to_login(); }
593

  
594
            $(".usermenu .username").text(synnefo.user.username);
595

  
589 596
            // images config
590 597
            synnefo.config.system_images_owners = {{ system_images_owners|safe }};
591 598
            synnefo.ui.init();
592
            synnefo.ui.main.bind("initial", function(){
599
            synnefo.ui.main.bind("ready", function(){
593 600
            });
594 601

  
595 602
        })
b/snf-cyclades-app/synnefo/ui/userdata/migrations/0003_auto__chg_field_publickeypair_fingerprint__chg_field_publickeypair_use.py
1
# encoding: utf-8
2
import datetime
3
from south.db import db
4
from south.v2 import SchemaMigration
5
from django.db import models
6

  
7
class Migration(SchemaMigration):
8
    
9
    def forwards(self, orm):
10
        
11
        # Changing field 'PublicKeyPair.fingerprint'
12
        db.alter_column('userdata_publickeypair', 'fingerprint', self.gf('django.db.models.fields.CharField')(max_length=100, blank=True))
13

  
14
        # Renaming column for 'PublicKeyPair.user' to match new field type.
15
        db.rename_column('userdata_publickeypair', 'user_id', 'user')
16
        # Changing field 'PublicKeyPair.user'
17
        db.alter_column('userdata_publickeypair', 'user', self.gf('django.db.models.fields.CharField')(max_length=100))
18

  
19
        # Removing index on 'PublicKeyPair', fields ['user']
20
        db.delete_index('userdata_publickeypair', ['user_id'])
21
    
22
    
23
    def backwards(self, orm):
24
        
25
        # Changing field 'PublicKeyPair.fingerprint'
26
        db.alter_column('userdata_publickeypair', 'fingerprint', self.gf('django.db.models.fields.CharField')(max_length=100))
27

  
28
        # Renaming column for 'PublicKeyPair.user' to match new field type.
29
        db.rename_column('userdata_publickeypair', 'user', 'user_id')
30
        # Changing field 'PublicKeyPair.user'
31
        db.alter_column('userdata_publickeypair', 'user_id', self.gf('django.db.models.fields.related.ForeignKey')(to=orm['db.SynnefoUser']))
32

  
33
        # Adding index on 'PublicKeyPair', fields ['user']
34
        db.create_index('userdata_publickeypair', ['user_id'])
35
    
36
    
37
    models = {
38
        'userdata.publickeypair': {
39
            'Meta': {'object_name': 'PublicKeyPair'},
40
            'content': ('django.db.models.fields.TextField', [], {}),
41
            'fingerprint': ('django.db.models.fields.CharField', [], {'max_length': '100', 'blank': 'True'}),
42
            'id': ('django.db.models.fields.AutoField', [], {'primary_key': 'True'}),
43
            'name': ('django.db.models.fields.CharField', [], {'max_length': '255'}),
44
            'user': ('django.db.models.fields.CharField', [], {'max_length': '100'})
45
        }
46
    }
47
    
48
    complete_apps = ['userdata']
b/snf-cyclades-app/synnefo/ui/userdata/models.py
43 43
from django.core.exceptions import ValidationError, NON_FIELD_ERRORS
44 44
from django.db.models.signals import pre_save
45 45

  
46
from synnefo.db import models as synnefo_models
47

  
48 46
try:
49 47
    from paramiko import rsakey, dsskey, SSHException
50 48
except:
b/snf-cyclades-app/synnefo/ui/views.py
76 76

  
77 77
SUPPORT_SSH_OS_LIST = getattr(settings, "UI_SUPPORT_SSH_OS_LIST",)
78 78
OS_CREATED_USERS = getattr(settings, "UI_OS_DEFAULT_USER_MAP")
79
LOGOUT_URL = getattr(settings, "LOGOUT_URL", settings.LOGIN_URL)
79
LOGOUT_URL = getattr(settings, "UI_LOGOUT_URL", '/im/authenticate')
80
LOGIN_URL = getattr(settings, "UI_LOGIN_URL", '/im/login')
81
AUTH_COOKIE_NAME = getattr(settings, "UI_AUTH_COOKIE_NAME", 'synnefo_user')
80 82

  
81 83
# UI behaviour settings
82 84
DELAY_ON_BLUR = getattr(settings, "UI_DELAY_ON_BLUR", True)
......
108 110
# extensions
109 111
ENABLE_GLANCE = getattr(settings, 'UI_ENABLE_GLANCE', True)
110 112
GLANCE_API_URL = getattr(settings, 'UI_GLANCE_API_URL', '/glance')
111
INVITATIONS_PER_PAGE = getattr(settings, "INVITATIONS_PER_PAGE", 10)
112 113
FEEDBACK_CONTACTS = getattr(settings, "FEEDBACK_CONTACTS", [])
113 114
FEEDBACK_EMAIL_FROM = settings.FEEDBACK_EMAIL_FROM
114 115

  
......
144 145
                # additional settings
145 146
               'image_icons': IMAGE_ICONS,
146 147
               'logout_redirect': LOGOUT_URL,
148
               'login_redirect': LOGIN_URL,
149
               'auth_cookie_name': AUTH_COOKIE_NAME,
147 150
               'suggested_flavors': json.dumps(SUGGESTED_FLAVORS),
148 151
               'suggested_roles': json.dumps(SUGGESTED_ROLES),
149 152
               'vm_image_common_metadata': json.dumps(VM_IMAGE_COMMON_METADATA),
150 153
               'synnefo_version': SYNNEFO_JS_LIB_VERSION,
151
               'invitations_per_page': INVITATIONS_PER_PAGE,
152 154
               'delay_on_blur': json.dumps(DELAY_ON_BLUR),
153 155
               'update_hidden_views': json.dumps(UPDATE_HIDDEN_VIEWS),
154 156
               'handle_window_exceptions': json.dumps(HANDLE_WINDOW_EXCEPTIONS),

Also available in: Unified diff