add gitlab login

This commit is contained in:
Сергей Маринкевич
2026-06-05 13:39:54 +07:00
parent b3756075f2
commit 3b332d2e0c
+174 -9
View File
@@ -1,13 +1,19 @@
import os import os
import pickle
import sqlite3 import sqlite3
from datetime import datetime, date from datetime import datetime, date
from typing import Tuple, Dict, List from typing import Tuple, Dict, List, Optional
import getpass import getpass
import requests
from bs4 import BeautifulSoup
from mattermostdriver import Driver from mattermostdriver import Driver
import pathlib import pathlib
import json import json
GITLAB_URL_DEFAULT = "https://mmgit.eltex-co.ru"
COOKIE_FILE_DEFAULT = "cookies.pkl"
def connect(host: str, login_token: str = None, username: str = None, password: str = None) -> Driver: def connect(host: str, login_token: str = None, username: str = None, password: str = None) -> Driver:
d = Driver({ d = Driver({
@@ -21,6 +27,117 @@ def connect(host: str, login_token: str = None, username: str = None, password:
return d return d
def _mattermost_url(host: str) -> str:
return f"https://{host}"
def _save_cookies(session: requests.Session, filename: str) -> None:
try:
with open(filename, "wb") as f:
pickle.dump(session.cookies, f)
except OSError as e:
print(f"Warning: failed to save cookies to {filename}: {e}")
def _load_cookies(session: requests.Session, filename: str) -> bool:
if not os.path.exists(filename):
return False
try:
with open(filename, "rb") as f:
session.cookies.update(pickle.load(f))
return True
except (OSError, pickle.PickleError) as e:
print(f"Warning: failed to load cookies from {filename}: {e}")
return False
def _remove_cookie_file(filename: str) -> None:
try:
if os.path.exists(filename):
os.remove(filename)
print(f"Removed invalid cookie file: {filename}")
except OSError as e:
print(f"Warning: failed to remove cookie file {filename}: {e}")
def _get_current_user(session: requests.Session, host: str) -> Optional[dict]:
try:
response = session.get(f"{_mattermost_url(host)}/api/v4/users/me", timeout=10)
return response.json() if response.status_code == 200 else None
except requests.exceptions.RequestException:
return None
def _login_to_mattermost_via_gitlab(session: requests.Session, host: str, gitlab_url: str,
username: str, password: str) -> bool:
login_url = f"{_mattermost_url(host)}/oauth/gitlab/login"
headers = {"User-Agent": "Mozilla/5.0"}
try:
response = session.get(login_url, headers=headers, allow_redirects=True, timeout=15)
response.raise_for_status()
if "/users/sign_in" in response.url or "/users/auth/ldapmain" in response.url:
soup = BeautifulSoup(response.text, "html.parser")
auth_form = soup.find("form", {"id": "new_ldap_user"})
if not auth_form:
return False
auth_token_input = auth_form.find("input", {"name": "authenticity_token"})
if not auth_token_input:
return False
auth_token = auth_token_input["value"]
payload = {"authenticity_token": auth_token, "username": username, "password": password}
callback_url = f"{gitlab_url}/users/auth/ldapmain/callback"
response = session.post(
callback_url, data=payload, headers={"Referer": response.url},
allow_redirects=True, timeout=15
)
response.raise_for_status()
if response.status_code == 200 and "/oauth/authorize" in response.url:
soup = BeautifulSoup(response.text, "html.parser")
redirect_link = soup.find("a", href=True)
if not redirect_link:
return False
response = session.get(
redirect_link["href"], headers={"Referer": response.url},
allow_redirects=True, timeout=15
)
response.raise_for_status()
except requests.exceptions.RequestException as e:
print(f"GitLab login failed: {e}")
return False
return "MMAUTHTOKEN" in session.cookies
def get_token_via_gitlab(host: str, gitlab_url: str, username: str, password: str,
cookie_file: str = COOKIE_FILE_DEFAULT) -> Optional[str]:
session = requests.Session()
if _load_cookies(session, cookie_file):
print("Checking saved GitLab/Mattermost session...")
if _get_current_user(session, host):
token = session.cookies.get("MMAUTHTOKEN")
if token:
print("Authenticated using saved cookies.")
return token
print("Saved cookies are invalid or expired.")
_remove_cookie_file(cookie_file)
print("Logging in to Mattermost via GitLab...")
if not _login_to_mattermost_via_gitlab(session, host, gitlab_url, username, password):
print("Failed to authenticate via GitLab.")
return None
_save_cookies(session, cookie_file)
print(f"Session saved to {cookie_file}")
return session.cookies.get("MMAUTHTOKEN")
def get_users(d: Driver) -> Tuple[Dict[str, str], str]: def get_users(d: Driver) -> Tuple[Dict[str, str], str]:
my_user = d.users.get_user("me") my_user = d.users.get_user("me")
my_username = my_user["username"] my_username = my_user["username"]
@@ -216,8 +333,10 @@ def complete_config(config: dict, config_filename: str = "config.json") -> dict:
print(f"Using login mode '{config['login_mode']}' from config") print(f"Using login mode '{config['login_mode']}' from config")
else: else:
login_mode = "" login_mode = ""
while login_mode not in ["password", "token"]: while login_mode not in ["password", "token", "gitlab"]:
login_mode = input("Please input login_mode 'password' or 'token' (=Gitlab Oauth): ") login_mode = input(
"Please input login_mode 'password', 'token', or 'gitlab' (GitLab OAuth): "
)
config["login_mode"] = login_mode config["login_mode"] = login_mode
config_changed = True config_changed = True
@@ -230,20 +349,66 @@ def complete_config(config: dict, config_filename: str = "config.json") -> dict:
config_changed = True config_changed = True
password = getpass.getpass("Enter your password (hidden): ") password = getpass.getpass("Enter your password (hidden): ")
elif config["login_mode"] == "gitlab":
if config.get("gitlab_url", False):
print(f"Using GitLab URL '{config['gitlab_url']}' from config")
else:
config["gitlab_url"] = input(
f"Please input GitLab URL [{GITLAB_URL_DEFAULT}]: "
) or GITLAB_URL_DEFAULT
config_changed = True
if config.get("username", False):
print(f"Using username '{config['username']}' from config")
else:
config["username"] = input("Please input your GitLab username: ")
config_changed = True
password = getpass.getpass("Enter your GitLab password (hidden): ")
token = get_token_via_gitlab(
config["host"], config["gitlab_url"], config["username"], password,
config.get("cookie_file", COOKIE_FILE_DEFAULT)
)
if not token:
raise SystemExit("GitLab authentication failed.")
config["token"] = token
config_changed = True
else: else:
if config.get("token", False): if config.get("token", False):
print(f"Using token '{config['token']}' from config") print(f"Using token '{config['token']}' from config")
else: else:
print("Are you logged-in into Mattermost using the Firefox Browser? " print("Token not found in config. Choose how to obtain MMAUTHTOKEN:")
"If so, token may be automatically extracted") print(" g - login via GitLab (username/password)")
print(" f - extract from Firefox cookies")
print(" m - enter token manually")
dec = "" dec = ""
while not (dec == "y" or dec == "n"): while dec not in ["g", "f", "m"]:
dec = input("Try to find token automatically? y/n: ") dec = input("Choice [g/f/m]: ")
token = None token = None
if dec == "y": if dec == "g":
if config.get("gitlab_url", False):
print(f"Using GitLab URL '{config['gitlab_url']}' from config")
else:
config["gitlab_url"] = input(
f"Please input GitLab URL [{GITLAB_URL_DEFAULT}]: "
) or GITLAB_URL_DEFAULT
config_changed = True
if config.get("username", False):
print(f"Using username '{config['username']}' from config")
else:
config["username"] = input("Please input your GitLab username: ")
config_changed = True
password = getpass.getpass("Enter your GitLab password (hidden): ")
token = get_token_via_gitlab(
config["host"], config["gitlab_url"], config["username"], password,
config.get("cookie_file", COOKIE_FILE_DEFAULT)
)
if not token:
raise SystemExit("GitLab authentication failed.")
elif dec == "f":
token = find_mmauthtoken_firefox(config["host"]) token = find_mmauthtoken_firefox(config["host"])
elif not token: if not token:
token = input("Please input your login token (MMAUTHTOKEN): ") token = input("Please input your login token (MMAUTHTOKEN): ")
config["token"] = token config["token"] = token
config_changed = True config_changed = True