From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from firstgate.proxmox.com (firstgate.proxmox.com [212.224.123.68]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits)) (No client certificate requested) by lists.proxmox.com (Postfix) with ESMTPS id 818B574967 for ; Tue, 22 Jun 2021 10:57:05 +0200 (CEST) Received: from firstgate.proxmox.com (localhost [127.0.0.1]) by firstgate.proxmox.com (Proxmox) with ESMTP id 92B3E24038 for ; Tue, 22 Jun 2021 10:56:34 +0200 (CEST) Received: from dev7.proxmox.com (unknown [94.136.29.99]) by firstgate.proxmox.com (Proxmox) with ESMTP id DE49223E6A for ; Tue, 22 Jun 2021 10:56:28 +0200 (CEST) Received: by dev7.proxmox.com (Postfix, from userid 0) id 940E180F5A; Tue, 22 Jun 2021 10:56:22 +0200 (CEST) From: Dietmar Maurer To: pbs-devel@lists.proxmox.com Date: Tue, 22 Jun 2021 10:56:16 +0200 Message-Id: <20210622085620.1551677-9-dietmar@proxmox.com> X-Mailer: git-send-email 2.30.2 In-Reply-To: <20210622085620.1551677-1-dietmar@proxmox.com> References: <20210622085620.1551677-1-dietmar@proxmox.com> MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-SPAM-LEVEL: Spam detection results: 0 AWL 0.684 Adjusted score from AWL reputation of From: address BAYES_00 -1.9 Bayes spam probability is 0 to 1% KAM_DMARC_STATUS 0.01 Test Rule for DKIM or SPF Failure with Strict Alignment RDNS_NONE 0.793 Delivered to internal network by a host with no rDNS SPF_HELO_NONE 0.001 SPF: HELO does not publish an SPF Record SPF_PASS -0.001 SPF: sender matches SPF record URIBL_BLOCKED 0.001 ADMINISTRATOR NOTICE: The query to URIBL was blocked. See http://wiki.apache.org/spamassassin/DnsBlocklists#dnsbl-block for more information. [domains.rs, access.rs, domain.rs] Subject: [pbs-devel] [PATH proxmox-backup v1 08/12] api: add openid-login endpoint X-BeenThere: pbs-devel@lists.proxmox.com X-Mailman-Version: 2.1.29 Precedence: list List-Id: Proxmox Backup Server development discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , X-List-Received-Date: Tue, 22 Jun 2021 08:57:05 -0000 --- 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 { + 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, } +impl OpenIdRealmConfig { + + pub fn authenticator(&self, redirect_url: &str) -> Result { + 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