From 34f956bc25e7cf419c6da7e111fb90424b7ac74b Mon Sep 17 00:00:00 2001 From: Dietmar Maurer Date: Wed, 30 Jan 2019 15:14:20 +0100 Subject: [PATCH] api2/access.rs: add ticket api --- src/api2.rs | 5 +- src/api2/access.rs | 86 ++++++++++++++++++++++++++++++ src/server/rest.rs | 4 +- www/Application.js | 33 +++++++++--- www/LoginView.js | 128 +++++++++++++++++++++++++++++++++++++++++++++ www/MainView.js | 10 ++++ www/Makefile | 1 + www/Utils.js | 6 +++ 8 files changed, 262 insertions(+), 11 deletions(-) create mode 100644 src/api2/access.rs create mode 100644 www/LoginView.js diff --git a/src/api2.rs b/src/api2.rs index 3f59b72d..42a55989 100644 --- a/src/api2.rs +++ b/src/api2.rs @@ -10,6 +10,7 @@ pub mod admin; pub mod node; mod version; mod subscription; +mod access; use lazy_static::lazy_static; use crate::tools::common_regex; @@ -79,13 +80,15 @@ pub fn router() -> Router { let route = Router::new() .get(ApiMethod::new( |_,_,_| Ok(json!([ - {"subdir": "config"}, + {"subdir": "access"}, {"subdir": "admin"}, + {"subdir": "config"}, {"subdir": "nodes"}, {"subdir": "subscription"}, {"subdir": "version"}, ])), ObjectSchema::new("Directory index."))) + .subdir("access", access::router()) .subdir("admin", admin::router()) .subdir("config", config::router()) .subdir("nodes", nodes) diff --git a/src/api2/access.rs b/src/api2/access.rs new file mode 100644 index 00000000..9c65d221 --- /dev/null +++ b/src/api2/access.rs @@ -0,0 +1,86 @@ +use failure::*; + +use crate::tools; +use crate::api::schema::*; +use crate::api::router::*; +use crate::tools::ticket::*; +use crate::auth_helpers::*; + +use serde_json::{json, Value}; + +fn authenticate_user(username: &str, password: &str) -> Result<(), Error> { + + if username == "root@pam" && password == "test" { + return Ok(()); + } + + bail!("inavlid credentials"); +} + +fn create_ticket( + param: Value, + _info: &ApiMethod, + rpcenv: &mut RpcEnvironment, +) -> Result { + + let username = tools::required_string_param(¶m, "username")?; + let password = tools::required_string_param(¶m, "password")?; + + match authenticate_user(username, password) { + Ok(_) => { + + let ticket = assemble_rsa_ticket( private_auth_key(), "PBS", None, None)?; + + let token = assemble_csrf_prevention_token(csrf_secret(), username); + + log::info!("successful auth for user '{}'", username); + + return Ok(json!({ + "username": username, + "ticket": ticket, + "CSRFPreventionToken": token, + })); + } + Err(err) => { + let client_ip = "unknown"; // $rpcenv->get_client_ip() || ''; + log::error!("authentication failure; rhost={} user={} msg={}", client_ip, username, err.to_string()); + bail!("authentication failure"); + } + } +} + +pub fn router() -> Router { + + let route = Router::new() + .get(ApiMethod::new( + |_,_,_| Ok(json!([ + {"subdir": "ticket"} + ])), + ObjectSchema::new("Directory index."))) + .subdir( + "ticket", + Router::new() + .post( + ApiMethod::new( + create_ticket, + ObjectSchema::new("Create or verify authentication ticket.") + .required( + "username", + StringSchema::new("User name.") + .max_length(64) + ) + .required( + "password", + StringSchema::new("The secret password. This can also be a valid ticket.") + ) + ).returns( + ObjectSchema::new("Returns authentication ticket with additional infos.") + .required("username", StringSchema::new("User name.")) + .required("ticket", StringSchema::new("Auth ticket.")) + .required("CSRFPreventionToken", StringSchema::new("Cross Site Request Forgery Prevention Token.")) + ).protected(true) + ) + ); + + route +} diff --git a/src/server/rest.rs b/src/server/rest.rs index 6df857eb..1a7831cc 100644 --- a/src/server/rest.rs +++ b/src/server/rest.rs @@ -247,8 +247,8 @@ fn handle_async_api_request( fn get_index() -> BoxFut { let nodename = tools::nodename(); - let username = "fakelogin"; // todo: implement real auth - let token = "abc"; + let username = ""; // fixme: implement real auth + let token = ""; let setup = json!({ "Setup": { "auth_cookie_name": "PBSAuthCookie" }, diff --git a/www/Application.js b/www/Application.js index 0bd0e6b9..71d92942 100644 --- a/www/Application.js +++ b/www/Application.js @@ -20,15 +20,30 @@ Ext.define('PBS.Application', { logout: function() { var me = this; - //Proxmox.Utils.authClear(); - //me.changeView('loginview', true); + Proxmox.Utils.authClear(); + me.changeView('loginview', true); }, changeView: function(view, skipCheck) { var me = this; - //? + PBS.view = view; + me.view = view; + + if (me.currentView != undefined) { + me.currentView.destroy(); + } + + me.currentView = Ext.create({ + xtype: view, + }); + if (skipCheck !== true) { + // fixme: + // Proxmox.Utils.checked_command(function() {}); // display subscription status + } }, + view: 'loginview', + launch: function() { var me = this; Ext.on('resize', me.realignWindows); @@ -36,11 +51,13 @@ Ext.define('PBS.Application', { var provider = new Ext.state.LocalStorageProvider({ prefix: 'ext-pbs-' }); Ext.state.Manager.setProvider(provider); - // fixme: show login window if not loggedin - - me.currentView = Ext.create({ - xtype: 'mainview' - }); + // show login window if not loggedin + var loggedin = Proxmox.Utils.authOK(); + if (!loggedin) { + me.changeView('loginview', true); + } else { + me.changeView('mainview', true); + } } }); diff --git a/www/LoginView.js b/www/LoginView.js new file mode 100644 index 00000000..bee3d514 --- /dev/null +++ b/www/LoginView.js @@ -0,0 +1,128 @@ +Ext.define('PBS.LoginView', { + extend: 'Ext.container.Container', + xtype: 'loginview', + + controller: { + xclass: 'Ext.app.ViewController', + + submitForm: function() { + var me = this; + var view = me.getView(); + var loginForm = me.lookupReference('loginForm'); + + if (loginForm.isValid()) { + if (loginForm.isVisible()) { + loginForm.mask(gettext('Please wait...'), 'x-mask-loading'); + } + loginForm.submit({ + success: function(form, action) { + // save login data and create cookie + PBS.Utils.updateLoginData(action.result.data); + PBS.app.changeView('mainview'); + }, + failure: function(form, action) { + loginForm.unmask(); + Ext.MessageBox.alert( + gettext('Error'), + gettext('Login failed. Please try again') + ); + } + }); + } + }, + + control: { + 'button[reference=loginButton]': { + click: 'submitForm' + } + } + }, + + plugins: 'viewport', + + layout: { + type: 'border' + }, + + items: [ + { + region: 'north', + xtype: 'container', + layout: { + type: 'hbox', + align: 'middle' + }, + margin: '2 5 2 5', + height: 38, + items: [ + { + xtype: 'proxmoxlogo' + }, + { + xtype: 'versioninfo', + makeApiCall: false + } + ] + }, + { + region: 'center' + }, + { + xtype: 'window', + closable: false, + resizable: false, + reference: 'loginwindow', + autoShow: true, + modal: true, + + defaultFocus: 'usernameField', + + layout: { + type: 'auto' + }, + + title: gettext('Proxmox Backup Server Login'), + + items: [ + { + xtype: 'form', + layout: { + type: 'form' + }, + defaultButton: 'loginButton', + url: '/api2/extjs/access/ticket', + reference: 'loginForm', + + fieldDefaults: { + labelAlign: 'right', + allowBlank: false + }, + + items: [ + { + xtype: 'textfield', + fieldLabel: gettext('User name'), + name: 'username', + itemId: 'usernameField', + reference: 'usernameField' + }, + { + xtype: 'textfield', + inputType: 'password', + fieldLabel: gettext('Password'), + name: 'password', + reference: 'passwordField' + } + ], + buttons: [ + { + text: gettext('Login'), + reference: 'loginButton', + formBind: true + } + ] + } + ] + } + ] +}); diff --git a/www/MainView.js b/www/MainView.js index d8bd53d7..74c968cf 100644 --- a/www/MainView.js +++ b/www/MainView.js @@ -82,10 +82,20 @@ Ext.define('PBS.MainView', { }, + logout: function() { + PBS.app.logout(); + }, + navigate: function(treelist, item) { this.redirectTo(item.get('path')); }, + control: { + 'button[reference=logoutButton]': { + click: 'logout' + } + }, + init: function(view) { var me = this; console.log("init"); diff --git a/www/Makefile b/www/Makefile index 1821dbf0..3f20507d 100644 --- a/www/Makefile +++ b/www/Makefile @@ -1,6 +1,7 @@ JSSRC= \ Utils.js \ Logo.js \ + LoginView.js \ VersionInfo.js \ SystemConfiguration.js \ Subscription.js \ diff --git a/www/Utils.js b/www/Utils.js index 4b5bafd3..258136a1 100644 --- a/www/Utils.js +++ b/www/Utils.js @@ -6,6 +6,12 @@ console.log("Starting Backup Server GUI"); Ext.define('PBS.Utils', { singleton: true, + updateLoginData: function(data) { + Proxmox.CSRFPreventionToken = data.CSRFPreventionToken; + Proxmox.UserName = data.username; + Ext.util.Cookies.set('PBSAuthCookie', data.ticket, null, '/', null, true ); + }, + constructor: function() { var me = this;