154 lines
4.9 KiB
Python
154 lines
4.9 KiB
Python
from authlib.integrations.flask_client import OAuth
|
|
from flask import Flask, jsonify, redirect, url_for, request
|
|
|
|
import requests
|
|
|
|
import secrets
|
|
import os
|
|
|
|
from configparser import ConfigParser
|
|
from pathlib import Path
|
|
|
|
config = ConfigParser()
|
|
config.read(Path(__file__).parent / "settings.ini")
|
|
|
|
app = Flask(__name__)
|
|
|
|
# Set a secret key for the Flask app
|
|
# This is used to encrypt the session cookie
|
|
# As we don't need to store any session data, we use a random string
|
|
|
|
app.secret_key = secrets.token_hex(32)
|
|
|
|
plesk_url = config["PLESK"].get(
|
|
"Domain", fallback=os.environ.get("PLESK_DOMAIN"))
|
|
|
|
# Configure Authlib with OIDC provider details
|
|
# Will use environment variables if values are not set in config
|
|
|
|
oauth = OAuth(app)
|
|
|
|
client_id = config["OIDC"].get(
|
|
"ClientID", fallback=os.environ.get("OIDC_CLIENT_ID"))
|
|
client_secret = config["OIDC"].get(
|
|
"ClientSecret", fallback=os.environ.get("OIDC_CLIENT_SECRET"))
|
|
token_url = config["OIDC"].get(
|
|
"TokenURL", fallback=os.environ.get("OIDC_TOKEN_URL"))
|
|
authorize_url = config["OIDC"].get(
|
|
"AuthorizeURL", fallback=os.environ.get("OIDC_AUTHORIZE_URL"))
|
|
jwks_url = config["OIDC"].get(
|
|
"JWKSURL", fallback=os.environ.get("OIDC_JWKS_URL"))
|
|
|
|
oauth.register(
|
|
name='oidc',
|
|
client_id=client_id,
|
|
client_secret=client_secret,
|
|
access_token_url=token_url,
|
|
authorize_url=authorize_url,
|
|
jwks_uri=jwks_url,
|
|
client_kwargs={
|
|
'scope': config["OIDC"].get("Scope", fallback="openid profile email"),
|
|
'token_endpoint_auth_method': 'client_secret_basic',
|
|
'token_placement': 'header'
|
|
},
|
|
)
|
|
|
|
# Define a route for the "home page"
|
|
# This will redirect to the OIDC provider's login page
|
|
|
|
|
|
@app.route('/')
|
|
def home():
|
|
redirect_uri = url_for('oidc_callback', _external=True)
|
|
return oauth.oidc.authorize_redirect(redirect_uri)
|
|
|
|
# Define a route for the OIDC provider's callback URL
|
|
# This will be called after the user has logged in
|
|
# It will then create a Plesk session for the user and redirect to the Plesk login page
|
|
|
|
|
|
@app.route('/oidc/callback')
|
|
def oidc_callback():
|
|
# Get user information from OIDC provider
|
|
token = oauth.oidc.authorize_access_token()
|
|
user_info = token["userinfo"]
|
|
|
|
# Display user's preferred_username
|
|
username = user_info.get('preferred_username')
|
|
|
|
# Get the user's IP address
|
|
# This needs to be the IP address the Plesk server sees
|
|
|
|
# Check if "SourceIP" is set in config
|
|
|
|
source_ip = config["OIDC"].get("SourceIP", fallback="auto")
|
|
|
|
# If "SourceIP" is set to "auto", get the IP address from the X-Forwarded-For header
|
|
# If the header is not set, use the remote address as seen by Flask
|
|
|
|
if source_ip == "auto":
|
|
source_ip = request.headers.get("X-Forwarded-For", request.remote_addr)
|
|
|
|
# If "SourceIP" is set to "public", get the plesklogin host's public IPv4 from ipify.org
|
|
|
|
if source_ip == "public":
|
|
source_ip = requests.get("https://api.ipify.org").text
|
|
|
|
# Otherwise, just use the value of "SourceIP" as the IP address
|
|
|
|
# Prepare a request to the Plesk API to create a session for the user
|
|
|
|
xml_data = f"""
|
|
<packet version="1.6.9.1">
|
|
<server>
|
|
<create_session>
|
|
<login>{username}</login>
|
|
<data>
|
|
<user_ip>{source_ip}</user_ip>
|
|
<source_server></source_server>
|
|
</data>
|
|
</create_session>
|
|
</server>
|
|
</packet>
|
|
"""
|
|
|
|
headers = {
|
|
"Content-Type": "text/xml",
|
|
"HTTP_PRETTY_PRINT": "TRUE", # Hey, it's pretty!
|
|
}
|
|
|
|
# Add the username and password to the request headers
|
|
|
|
plesk_login = config["PLESK"].get(
|
|
"Username", fallback=os.environ.get("PLESK_USERNAME"))
|
|
plesk_password = config["PLESK"].get(
|
|
"Password", fallback=os.environ.get("PLESK_PASSWORD"))
|
|
|
|
headers["HTTP_AUTH_LOGIN"] = plesk_login
|
|
headers["HTTP_AUTH_PASSWD"] = plesk_password
|
|
|
|
# If "VerifySSL" is set to "False" in the configuration, disable SSL verification
|
|
|
|
verify = config["PLESK"].getboolean("VerifySSL", fallback=True)
|
|
|
|
response = requests.post(
|
|
f"https://{plesk_url}/enterprise/control/agent.php", headers=headers, data=xml_data, verify=verify)
|
|
|
|
# Extract the session ID from the response XML or raise an exception if the request failed
|
|
|
|
if response.status_code == 200:
|
|
response_xml = response.content.decode()
|
|
# Extract the session ID from the response XML
|
|
session_id = response_xml.split("<id>")[1].split("</id>")[0]
|
|
else:
|
|
print("Error:", response.status_code, response.content.decode())
|
|
raise Exception()
|
|
|
|
# Redirect to the Plesk login page with the session ID
|
|
|
|
return redirect(f"https://{plesk_url}/enterprise/rsession_init.php?PLESKSESSID={session_id}")
|
|
|
|
|
|
if __name__ == '__main__':
|
|
app.run(debug=config["FLASK"].getboolean(
|
|
"Debug", fallback=bool(os.environ.get("FLASK_DEBUG", False))))
|