2020-07-12 18:53:04 +00:00
|
|
|
# Handles authentication
|
2020-04-11 14:20:25 +00:00
|
|
|
|
|
|
|
from flask_httpauth import HTTPBasicAuth
|
2020-06-21 19:29:53 +00:00
|
|
|
from itsdangerous import (
|
|
|
|
TimedJSONWebSignatureSerializer as Serializer,
|
|
|
|
BadSignature,
|
|
|
|
SignatureExpired,
|
|
|
|
)
|
2020-04-11 14:20:25 +00:00
|
|
|
from passlib.apps import custom_app_context as pwd_context
|
|
|
|
import uuid
|
2020-06-16 19:07:47 +00:00
|
|
|
from jellyfin_accounts import config, app, g
|
|
|
|
from jellyfin_accounts import auth_log as log
|
2020-04-25 16:20:46 +00:00
|
|
|
from jellyfin_accounts.jf_api import Jellyfin
|
|
|
|
from jellyfin_accounts.web_api import jf
|
2020-04-11 14:20:25 +00:00
|
|
|
|
2020-06-21 19:29:53 +00:00
|
|
|
auth_jf = Jellyfin(
|
|
|
|
config["jellyfin"]["server"],
|
|
|
|
config["jellyfin"]["client"],
|
|
|
|
config["jellyfin"]["version"],
|
|
|
|
config["jellyfin"]["device"],
|
|
|
|
config["jellyfin"]["device_id"] + "_authClient",
|
|
|
|
)
|
2020-04-25 16:20:46 +00:00
|
|
|
|
2020-06-21 19:29:53 +00:00
|
|
|
|
|
|
|
class Account:
|
2020-04-25 16:20:46 +00:00
|
|
|
def __init__(self, username=None, password=None):
|
2020-04-11 14:20:25 +00:00
|
|
|
self.username = username
|
2020-04-25 16:20:46 +00:00
|
|
|
if password is not None:
|
|
|
|
self.password_hash = pwd_context.hash(password)
|
|
|
|
self.id = str(uuid.uuid4())
|
|
|
|
self.jf = False
|
|
|
|
elif username is not None:
|
2020-06-21 19:29:53 +00:00
|
|
|
jf.authenticate(
|
|
|
|
config["jellyfin"]["username"], config["jellyfin"]["password"]
|
|
|
|
)
|
|
|
|
self.id = jf.getUsers(self.username, public=False)["Id"]
|
2020-04-25 16:20:46 +00:00
|
|
|
self.jf = True
|
2020-06-21 19:29:53 +00:00
|
|
|
|
2020-04-11 14:20:25 +00:00
|
|
|
def verify_password(self, password):
|
2020-04-25 16:20:46 +00:00
|
|
|
if not self.jf:
|
|
|
|
return pwd_context.verify(password, self.password_hash)
|
|
|
|
else:
|
|
|
|
try:
|
2020-05-09 21:10:30 +00:00
|
|
|
return auth_jf.authenticate(self.username, password)
|
2020-04-25 16:20:46 +00:00
|
|
|
except Jellyfin.AuthenticationError:
|
|
|
|
return False
|
2020-06-21 19:29:53 +00:00
|
|
|
|
2020-04-11 14:20:25 +00:00
|
|
|
def generate_token(self, expiration=1200):
|
2020-06-21 19:29:53 +00:00
|
|
|
s = Serializer(app.config["SECRET_KEY"], expires_in=expiration)
|
2020-04-25 16:20:46 +00:00
|
|
|
log.debug(self.id)
|
2020-06-21 19:29:53 +00:00
|
|
|
return s.dumps({"id": self.id})
|
2020-04-11 14:20:25 +00:00
|
|
|
|
|
|
|
@staticmethod
|
2020-04-25 16:20:46 +00:00
|
|
|
def verify_token(token, accounts):
|
2020-06-21 19:29:53 +00:00
|
|
|
log.debug(f"verifying token {token}")
|
|
|
|
s = Serializer(app.config["SECRET_KEY"])
|
2020-04-11 14:20:25 +00:00
|
|
|
try:
|
|
|
|
data = s.loads(token)
|
|
|
|
except SignatureExpired:
|
|
|
|
return None
|
|
|
|
except BadSignature:
|
|
|
|
return None
|
2020-06-21 19:29:53 +00:00
|
|
|
if config.getboolean("ui", "jellyfin_login"):
|
2020-04-25 16:20:46 +00:00
|
|
|
for account in accounts:
|
2020-06-21 19:29:53 +00:00
|
|
|
if data["id"] == accounts[account].id:
|
2020-04-25 16:20:46 +00:00
|
|
|
return account
|
|
|
|
else:
|
2020-06-21 19:29:53 +00:00
|
|
|
return accounts["adminAccount"]
|
2020-04-25 16:20:46 +00:00
|
|
|
|
2020-04-11 14:20:25 +00:00
|
|
|
|
|
|
|
auth = HTTPBasicAuth()
|
|
|
|
|
2020-04-25 16:20:46 +00:00
|
|
|
accounts = {}
|
2020-04-11 14:20:25 +00:00
|
|
|
|
2020-06-21 19:29:53 +00:00
|
|
|
if config.getboolean("ui", "jellyfin_login"):
|
|
|
|
log.debug("Using jellyfin for admin authentication")
|
2020-04-26 18:44:31 +00:00
|
|
|
else:
|
2020-06-21 19:29:53 +00:00
|
|
|
log.debug("Using configured login details for admin authentication")
|
|
|
|
accounts["adminAccount"] = Account(
|
|
|
|
config["ui"]["username"], config["ui"]["password"]
|
|
|
|
)
|
2020-04-11 14:20:25 +00:00
|
|
|
|
|
|
|
|
|
|
|
@auth.verify_password
|
|
|
|
def verify_password(username, password):
|
2020-04-25 16:20:46 +00:00
|
|
|
user = None
|
|
|
|
verified = False
|
2020-06-21 19:29:53 +00:00
|
|
|
log.debug("Verifying auth")
|
|
|
|
if config.getboolean("ui", "jellyfin_login"):
|
2020-04-25 16:20:46 +00:00
|
|
|
try:
|
|
|
|
jf_user = jf.getUsers(username, public=False)
|
2020-06-21 19:29:53 +00:00
|
|
|
id = jf_user["Id"]
|
2020-04-25 16:20:46 +00:00
|
|
|
user = accounts[id]
|
|
|
|
except KeyError:
|
2020-06-21 19:29:53 +00:00
|
|
|
if config.getboolean("ui", "admin_only"):
|
|
|
|
if jf_user["Policy"]["IsAdministrator"]:
|
2020-04-25 16:20:46 +00:00
|
|
|
user = Account(username)
|
|
|
|
accounts[id] = user
|
|
|
|
else:
|
2020-06-21 19:29:53 +00:00
|
|
|
log.debug(f"User {username} not admin.")
|
2020-04-25 16:20:46 +00:00
|
|
|
return False
|
|
|
|
else:
|
|
|
|
user = Account(username)
|
|
|
|
accounts[id] = user
|
|
|
|
except Jellyfin.UserNotFoundError:
|
|
|
|
user = Account().verify_token(username, accounts)
|
|
|
|
if user:
|
|
|
|
verified = True
|
|
|
|
if not user:
|
2020-06-21 19:29:53 +00:00
|
|
|
log.debug(f"User {username} not found on Jellyfin")
|
2020-04-25 16:20:46 +00:00
|
|
|
return False
|
|
|
|
else:
|
2020-06-21 19:29:53 +00:00
|
|
|
user = accounts["adminAccount"]
|
|
|
|
verified = Account().verify_token(username, accounts)
|
2020-04-25 16:20:46 +00:00
|
|
|
if not verified:
|
|
|
|
if username == user.username and user.verify_password(password):
|
|
|
|
g.user = user
|
2020-04-12 20:25:27 +00:00
|
|
|
log.debug("HTTPAuth Allowed")
|
2020-04-11 14:20:25 +00:00
|
|
|
return True
|
|
|
|
else:
|
2020-04-12 20:25:27 +00:00
|
|
|
log.debug("HTTPAuth Denied")
|
2020-04-11 14:20:25 +00:00
|
|
|
return False
|
2020-04-25 16:20:46 +00:00
|
|
|
g.user = user
|
2020-04-12 20:25:27 +00:00
|
|
|
log.debug("HTTPAuth Allowed")
|
2020-04-11 14:20:25 +00:00
|
|
|
return True
|