mirror of
https://github.com/freedombox/FreedomBox.git
synced 2026-04-22 10:01:45 +00:00
This is a temporary measure since no permanent solution was found for the cases where auth_pubtkt cookie refresh fails when an application sends POST requests only during the time a cookie refresh must happen. e.g. transmission and tt-rss This at least makes the above applications usable for an hour. And then the user must refresh the application on their web browser. This does not affect the mobile apps. The only other option for now is to disable SSO for the above applications till we implement a better SSO solution which is undesirable since our users are already used to the SSO feature. Signed-off-by: Joseph Nuthalapati <njoseph@thoughtworks.com> Reviewed-by: James Valleroy <jvalleroy@mailbox.org>
154 lines
5.1 KiB
Python
Executable File
154 lines
5.1 KiB
Python
Executable File
#!/usr/bin/python3
|
|
#
|
|
# This file is part of Plinth.
|
|
#
|
|
# This program is free software: you can redistribute it and/or modify
|
|
# it under the terms of the GNU Affero General Public License as
|
|
# published by the Free Software Foundation, either version 3 of the
|
|
# License, or (at your option) any later version.
|
|
#
|
|
# This program is distributed in the hope that it will be useful,
|
|
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
# GNU Affero General Public License for more details.
|
|
#
|
|
# You should have received a copy of the GNU Affero General Public License
|
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
#
|
|
"""
|
|
Module with utilities to generate a auth_pubtkt ticket and
|
|
sign it with the FreedomBox server's private key.
|
|
"""
|
|
|
|
import argparse
|
|
import base64
|
|
import datetime
|
|
from OpenSSL import crypto
|
|
import os
|
|
|
|
from plinth import action_utils
|
|
|
|
KEYS_DIRECTORY = '/etc/apache2/auth-pubtkt-keys'
|
|
|
|
|
|
def parse_arguments():
|
|
""" Return parsed command line arguments as dictionary. """
|
|
parser = argparse.ArgumentParser()
|
|
subparsers = parser.add_subparsers(dest='subcommand', help='Sub command')
|
|
|
|
subparsers.add_parser(
|
|
'create-key-pair',
|
|
help='create a key pair for the apache server '
|
|
'to sign auth_pubtkt tickets')
|
|
subparsers.add_parser(
|
|
'enable-mod', help='enabled the Apache module auth_pubtkt')
|
|
gen_tkt = subparsers.add_parser(
|
|
'generate-ticket', help='generate auth_pubtkt ticket')
|
|
gen_tkt.add_argument('--uid', help='username of the user')
|
|
gen_tkt.add_argument(
|
|
'--private-key-file',
|
|
help='path of the private key file of the server')
|
|
gen_tkt.add_argument(
|
|
'--tokens', help='tokens, usually containing the user groups')
|
|
|
|
subparsers.required = True
|
|
return parser.parse_args()
|
|
|
|
|
|
def subcommand_enable_mod(_):
|
|
"""Safety check to make sure auth_pubtkt is enabled"""
|
|
action_utils.webserver_enable('auth_pubtkt', kind='module')
|
|
|
|
|
|
def subcommand_create_key_pair(_):
|
|
"""Create public/private key pair for signing the auth_pubtkt
|
|
tickets.
|
|
"""
|
|
private_key_file = os.path.join(KEYS_DIRECTORY, 'privkey.pem')
|
|
public_key_file = os.path.join(KEYS_DIRECTORY, 'pubkey.pem')
|
|
|
|
os.path.exists(KEYS_DIRECTORY) or os.mkdir(KEYS_DIRECTORY)
|
|
|
|
if not all([
|
|
os.path.exists(key_file)
|
|
for key_file in [public_key_file, private_key_file]
|
|
]):
|
|
pkey = crypto.PKey()
|
|
pkey.generate_key(crypto.TYPE_RSA, 4096)
|
|
|
|
with open(private_key_file, 'w') as priv_key_file:
|
|
priv_key = crypto.dump_privatekey(crypto.FILETYPE_PEM,
|
|
pkey).decode()
|
|
priv_key_file.write(priv_key)
|
|
|
|
with open(public_key_file, 'w') as pub_key_file:
|
|
pub_key = crypto.dump_publickey(crypto.FILETYPE_PEM, pkey).decode()
|
|
pub_key_file.write(pub_key)
|
|
|
|
for fil in [public_key_file, private_key_file]:
|
|
os.chmod(fil, 0o440)
|
|
|
|
|
|
def create_ticket(pkey,
|
|
uid,
|
|
validuntil,
|
|
ip=None,
|
|
tokens=None,
|
|
udata=None,
|
|
graceperiod=None,
|
|
extra_fields=None):
|
|
"""Create and return a signed mod_auth_pubtkt ticket."""
|
|
fields = [
|
|
'uid={}'.format(uid), 'validuntil={}'.format(validuntil, type='d'), ip
|
|
and 'cip={}'.format(ip), tokens and 'tokens={}'.format(tokens),
|
|
graceperiod and 'graceperiod={}'.format(graceperiod, type='d'), udata
|
|
and 'udata={}'.format(udata), extra_fields
|
|
and ';'.join(['{}={}'.format(k, v) for k, v in extra_fields])
|
|
]
|
|
data = ';'.join(filter(None, fields))
|
|
signature = 'sig={}'.format(sign(pkey, data))
|
|
return ';'.join([data, signature])
|
|
|
|
|
|
def sign(pkey, data):
|
|
"""Calculates and returns ticket's signature."""
|
|
sig = crypto.sign(pkey, data, 'sha512')
|
|
return base64.b64encode(sig).decode()
|
|
|
|
|
|
def subcommand_generate_ticket(arguments):
|
|
"""Generate a mod_auth_pubtkt ticket using login credentials."""
|
|
uid = arguments.uid
|
|
private_key_file = arguments.private_key_file
|
|
tokens = arguments.tokens
|
|
with open(private_key_file, 'r') as fil:
|
|
pkey = crypto.load_privatekey(crypto.FILETYPE_PEM, fil.read().encode())
|
|
valid_until = minutes_from_now(60)
|
|
grace_period = minutes_from_now(55)
|
|
print(create_ticket(pkey, uid, valid_until, tokens=tokens,
|
|
graceperiod=grace_period))
|
|
|
|
|
|
def minutes_from_now(minutes):
|
|
"""Return a timestamp at the given number of minutes from now."""
|
|
return seconds_from_now(minutes * 60)
|
|
|
|
|
|
def seconds_from_now(seconds):
|
|
"""Return a timestamp at the given number of seconds from now."""
|
|
return (
|
|
datetime.datetime.now() + datetime.timedelta(0, seconds)).timestamp()
|
|
|
|
|
|
def main():
|
|
"""Parse arguments and perform all duties."""
|
|
arguments = parse_arguments()
|
|
|
|
subcommand = arguments.subcommand.replace('-', '_')
|
|
subcommand_method = globals()['subcommand_' + subcommand]
|
|
subcommand_method(arguments)
|
|
|
|
|
|
if __name__ == '__main__':
|
|
main()
|