api2/access.rs: add ticket api
This commit is contained in:
parent
1bf446a33e
commit
34f956bc25
@ -10,6 +10,7 @@ pub mod admin;
|
|||||||
pub mod node;
|
pub mod node;
|
||||||
mod version;
|
mod version;
|
||||||
mod subscription;
|
mod subscription;
|
||||||
|
mod access;
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use crate::tools::common_regex;
|
use crate::tools::common_regex;
|
||||||
@ -79,13 +80,15 @@ pub fn router() -> Router {
|
|||||||
let route = Router::new()
|
let route = Router::new()
|
||||||
.get(ApiMethod::new(
|
.get(ApiMethod::new(
|
||||||
|_,_,_| Ok(json!([
|
|_,_,_| Ok(json!([
|
||||||
{"subdir": "config"},
|
{"subdir": "access"},
|
||||||
{"subdir": "admin"},
|
{"subdir": "admin"},
|
||||||
|
{"subdir": "config"},
|
||||||
{"subdir": "nodes"},
|
{"subdir": "nodes"},
|
||||||
{"subdir": "subscription"},
|
{"subdir": "subscription"},
|
||||||
{"subdir": "version"},
|
{"subdir": "version"},
|
||||||
])),
|
])),
|
||||||
ObjectSchema::new("Directory index.")))
|
ObjectSchema::new("Directory index.")))
|
||||||
|
.subdir("access", access::router())
|
||||||
.subdir("admin", admin::router())
|
.subdir("admin", admin::router())
|
||||||
.subdir("config", config::router())
|
.subdir("config", config::router())
|
||||||
.subdir("nodes", nodes)
|
.subdir("nodes", nodes)
|
||||||
|
86
src/api2/access.rs
Normal file
86
src/api2/access.rs
Normal file
@ -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<Value, Error> {
|
||||||
|
|
||||||
|
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
|
||||||
|
}
|
@ -247,8 +247,8 @@ fn handle_async_api_request(
|
|||||||
fn get_index() -> BoxFut {
|
fn get_index() -> BoxFut {
|
||||||
|
|
||||||
let nodename = tools::nodename();
|
let nodename = tools::nodename();
|
||||||
let username = "fakelogin"; // todo: implement real auth
|
let username = ""; // fixme: implement real auth
|
||||||
let token = "abc";
|
let token = "";
|
||||||
|
|
||||||
let setup = json!({
|
let setup = json!({
|
||||||
"Setup": { "auth_cookie_name": "PBSAuthCookie" },
|
"Setup": { "auth_cookie_name": "PBSAuthCookie" },
|
||||||
|
@ -20,15 +20,30 @@ Ext.define('PBS.Application', {
|
|||||||
|
|
||||||
logout: function() {
|
logout: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
//Proxmox.Utils.authClear();
|
Proxmox.Utils.authClear();
|
||||||
//me.changeView('loginview', true);
|
me.changeView('loginview', true);
|
||||||
},
|
},
|
||||||
|
|
||||||
changeView: function(view, skipCheck) {
|
changeView: function(view, skipCheck) {
|
||||||
var me = this;
|
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() {
|
launch: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
Ext.on('resize', me.realignWindows);
|
Ext.on('resize', me.realignWindows);
|
||||||
@ -36,11 +51,13 @@ Ext.define('PBS.Application', {
|
|||||||
var provider = new Ext.state.LocalStorageProvider({ prefix: 'ext-pbs-' });
|
var provider = new Ext.state.LocalStorageProvider({ prefix: 'ext-pbs-' });
|
||||||
Ext.state.Manager.setProvider(provider);
|
Ext.state.Manager.setProvider(provider);
|
||||||
|
|
||||||
// fixme: show login window if not loggedin
|
// show login window if not loggedin
|
||||||
|
var loggedin = Proxmox.Utils.authOK();
|
||||||
me.currentView = Ext.create({
|
if (!loggedin) {
|
||||||
xtype: 'mainview'
|
me.changeView('loginview', true);
|
||||||
});
|
} else {
|
||||||
|
me.changeView('mainview', true);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
128
www/LoginView.js
Normal file
128
www/LoginView.js
Normal file
@ -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
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
});
|
@ -82,10 +82,20 @@ Ext.define('PBS.MainView', {
|
|||||||
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
logout: function() {
|
||||||
|
PBS.app.logout();
|
||||||
|
},
|
||||||
|
|
||||||
navigate: function(treelist, item) {
|
navigate: function(treelist, item) {
|
||||||
this.redirectTo(item.get('path'));
|
this.redirectTo(item.get('path'));
|
||||||
},
|
},
|
||||||
|
|
||||||
|
control: {
|
||||||
|
'button[reference=logoutButton]': {
|
||||||
|
click: 'logout'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
init: function(view) {
|
init: function(view) {
|
||||||
var me = this;
|
var me = this;
|
||||||
console.log("init");
|
console.log("init");
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
JSSRC= \
|
JSSRC= \
|
||||||
Utils.js \
|
Utils.js \
|
||||||
Logo.js \
|
Logo.js \
|
||||||
|
LoginView.js \
|
||||||
VersionInfo.js \
|
VersionInfo.js \
|
||||||
SystemConfiguration.js \
|
SystemConfiguration.js \
|
||||||
Subscription.js \
|
Subscription.js \
|
||||||
|
@ -6,6 +6,12 @@ console.log("Starting Backup Server GUI");
|
|||||||
Ext.define('PBS.Utils', {
|
Ext.define('PBS.Utils', {
|
||||||
singleton: true,
|
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() {
|
constructor: function() {
|
||||||
var me = this;
|
var me = this;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user