[pbs-devel] [PATH proxmox-backup v1 08/12] api: add openid-login endpoint
Dietmar Maurer
dietmar at proxmox.com
Tue Jun 22 10:56:16 CEST 2021
---
src/api2/access.rs | 91 +++++++++++++++++++++++++++++++++++++++
src/api2/access/domain.rs | 2 +-
src/config/domains.rs | 14 ++++++
3 files changed, 106 insertions(+), 1 deletion(-)
diff --git a/src/api2/access.rs b/src/api2/access.rs
index 46725c97..5abb5cb4 100644
--- a/src/api2/access.rs
+++ b/src/api2/access.rs
@@ -5,16 +5,20 @@ use anyhow::{bail, format_err, Error};
use serde_json::{json, Value};
use std::collections::HashMap;
use std::collections::HashSet;
+use std::convert::TryFrom;
use proxmox::api::router::{Router, SubdirMap};
use proxmox::api::{api, Permission, RpcEnvironment};
use proxmox::{http_err, list_subdirs_api_method};
use proxmox::{identity, sortable};
+use proxmox_openid::OpenIdAuthenticator;
+
use crate::api2::types::*;
use crate::auth_helpers::*;
use crate::server::ticket::ApiTicket;
use crate::tools::ticket::{self, Empty, Ticket};
+use crate::config::domains::OpenIdRealmConfig;
use crate::config::acl as acl_config;
use crate::config::acl::{PRIVILEGES, PRIV_PERMISSIONS_MODIFY, PRIV_SYS_AUDIT};
@@ -235,6 +239,92 @@ pub fn create_ticket(
}
}
+#[api(
+ input: {
+ properties: {
+ state: {
+ description: "OpenId state.",
+ type: String,
+ },
+ code: {
+ description: "OpenId authorization code.",
+ type: String,
+ },
+ "redirect-url": {
+ description: "Redirection Url. The client should set this to used server url.",
+ type: String,
+ },
+ },
+ },
+ returns: {
+ properties: {
+ username: {
+ type: String,
+ description: "User name.",
+ },
+ ticket: {
+ type: String,
+ description: "Auth ticket.",
+ },
+ CSRFPreventionToken: {
+ type: String,
+ description: "Cross Site Request Forgery Prevention Token.",
+ },
+ },
+ },
+ protected: true,
+ access: {
+ permission: &Permission::World,
+ },
+)]
+/// Verify OpenID authorization code and create a ticket
+///
+/// Returns: An authentication ticket with additional infos.
+pub fn openid_login(
+ state: String,
+ code: String,
+ redirect_url: String,
+ _rpcenv: &mut dyn RpcEnvironment,
+) -> Result<Value, Error> {
+ let user_info = CachedUserInfo::new()?;
+
+ let backup_user = crate::backup::backup_user()?;
+
+ let (realm, private_auth_state) =
+ OpenIdAuthenticator::verify_public_auth_state(&state, backup_user.uid)?;
+
+ let (domains, _digest) = crate::config::domains::config()?;
+ let config: OpenIdRealmConfig = domains.lookup("openid", &realm)?;
+
+ let open_id = config.authenticator(&redirect_url)?;
+
+ let info = open_id.verify_authorization_code(&code, &private_auth_state)?;
+
+ // eprintln!("VERIFIED {} {:?} {:?}", info.subject().as_str(), info.name(), info.email());
+
+ // fixme: allow to use other attributes
+ let unique_name = info.subject().as_str();
+
+ let user_id = Userid::try_from(format!("{}@{}", unique_name, realm))?;
+
+ if !user_info.is_active_user_id(&user_id) {
+ bail!("user account '{}' disabled or expired.", user_id);
+ }
+
+ let api_ticket = ApiTicket::full(user_id.clone());
+ let ticket = Ticket::new("PBS", &api_ticket)?.sign(private_auth_key(), None)?;
+ let token = assemble_csrf_prevention_token(csrf_secret(), &user_id);
+
+ crate::server::rest::auth_logger()?
+ .log(format!("successful auth for user '{}'", user_id));
+
+ Ok(json!({
+ "username": user_id,
+ "ticket": ticket,
+ "CSRFPreventionToken": token,
+ }))
+}
+
#[api(
protected: true,
input: {
@@ -423,6 +513,7 @@ const SUBDIRS: SubdirMap = &sorted!([
&Router::new().get(&API_METHOD_LIST_PERMISSIONS)
),
("ticket", &Router::new().post(&API_METHOD_CREATE_TICKET)),
+ ("openid-login", &Router::new().post(&API_METHOD_OPENID_LOGIN)),
("domains", &domain::ROUTER),
("roles", &role::ROUTER),
("users", &user::ROUTER),
diff --git a/src/api2/access/domain.rs b/src/api2/access/domain.rs
index 3c9e3615..b325ae63 100644
--- a/src/api2/access/domain.rs
+++ b/src/api2/access/domain.rs
@@ -4,7 +4,7 @@ use anyhow::{Error};
use serde_json::{json, Value};
-use proxmox::api::{api, Permission};
+use proxmox::api::{api, Permission, RpcEnvironment};
use proxmox::api::router::Router;
use crate::api2::types::*;
diff --git a/src/config/domains.rs b/src/config/domains.rs
index ce3f6f23..007cf357 100644
--- a/src/config/domains.rs
+++ b/src/config/domains.rs
@@ -3,6 +3,8 @@ use lazy_static::lazy_static;
use std::collections::HashMap;
use serde::{Serialize, Deserialize};
+use proxmox_openid::{OpenIdAuthenticator, OpenIdConfig};
+
use proxmox::api::{
api,
schema::*,
@@ -65,6 +67,18 @@ pub struct OpenIdRealmConfig {
pub comment: Option<String>,
}
+impl OpenIdRealmConfig {
+
+ pub fn authenticator(&self, redirect_url: &str) -> Result<OpenIdAuthenticator, Error> {
+ let config = OpenIdConfig {
+ issuer_url: self.issuer_url.clone(),
+ client_id: self.client_id.clone(),
+ client_key: self.client_key.clone(),
+ };
+ OpenIdAuthenticator::discover(&config, redirect_url)
+ }
+}
+
fn init() -> SectionConfig {
let obj_schema = match OpenIdRealmConfig::API_SCHEMA {
Schema::Object(ref obj_schema) => obj_schema,
--
2.30.2
More information about the pbs-devel
mailing list