Skip to content

Commit

Permalink
Merge pull request #19 from datasetu/Test
Browse files Browse the repository at this point in the history
Minor refactoring
  • Loading branch information
pct960 authored Apr 21, 2021
2 parents 20dcf9c + f30841d commit d3fdfec
Show file tree
Hide file tree
Showing 10 changed files with 243 additions and 222 deletions.
220 changes: 30 additions & 190 deletions auth-backend/auth-backend.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,217 +3,50 @@
__author__ = "Vishwajeet Mishra <[email protected]>"

# Purpose: Main application file
import json, os
from flask import Flask, request, Response
from urllib.parse import unquote_plus, quote_plus, urlparse
from requests import post
import datetime
import config
import sys
import watchdog.events
from urllib.parse import unquote_plus, urlparse
import config, utils
import sys, logging
import watchdog.observers


class Handler(watchdog.events.PatternMatchingEventHandler):
def __init__(self):
# Set the patterns for PatternMatchingEventHandler
watchdog.events.PatternMatchingEventHandler.__init__(self, patterns=['*.flv'],
ignore_directories=True, case_sensitive=False)
app = Flask(__name__)

def on_created(self, event):
src = event.src_path

if "%252F" not in src:
return

record_name = src.split("record/")[1]
target = config.RECORD_TAR_DIR
record_name_split = record_name.split("%252F")

for segment in range(len(record_name_split) - 1):
target += "/" + record_name_split[segment]

if not os.path.exists(target):
os.makedirs(target)

recording_unique_id_split = record_name_split[-1].rsplit("-",1)

if len(recording_unique_id_split) == 2:
target += '/' + recording_unique_id_split[0]

if not os.path.exists(target):
os.makedirs(target)

target += '/' + recording_unique_id_split[1]

if not os.path.islink(target):
os.symlink(src, target)


app = Flask(__name__, template_folder='.')

token_cache = {}


def is_string_safe(string, exceptions="/.@-"):
if (not string or type(string) != str):
return False

if (len(string) == 0 or len(string) > config.MAX_SAFE_STRING_LEN):
return False

for ch in string:
if (
(ch >= "a" and ch <= "z") or
(ch >= "A" and ch <= "Z") or
(ch >= "0" and ch <= "9")
):
continue

if (exceptions.find(ch) == -1):
return False

return True


def is_valid_token(token):
if (not is_string_safe(token)):
return False

split = token.split("/")

if (len(split) != 2):
return False

issued_by = split[0]
# issued_to = split[1]
random_hex = split[1]

if (issued_by != config.AUTH_SERVER_NAME):
return False

if (len(random_hex) != config.TOKEN_LEN_HEX):
return False

return True


def symlink(resource_id):
resource_id_split = resource_id.split('/')

hls_src_dir = config.HLS_SRC_DIR

src = hls_src_dir + '/' + quote_plus(resource_id)

for segment in range(len(resource_id_split) - 1):
hls_src_dir += '/' + resource_id_split[segment]

if not os.path.exists(hls_src_dir):
os.makedirs(hls_src_dir)

hls_src_dir += '/' + resource_id_split[-1]

if not os.path.islink(hls_src_dir):
os.symlink(src, hls_src_dir)


def auth(introspect_response, resource_id, call):
if (not isinstance(introspect_response, dict) or not introspect_response or 'request' not in introspect_response):
print("Request not found in body.", file=sys.stderr)
return False

for r in introspect_response['request']:
if (not r['scopes']):
print("Scopes not found in body.", file=sys.stderr)
return False

if (r['id'] != resource_id):
continue

if (call == 'play'):
if ("read" not in r['scopes']):
print("Read scope is not assigned.", file=sys.stderr)
return False

elif (call == 'publish'):
if ("write" not in r['scopes']):
print("Write Scope is not assigned.", file=sys.stderr)
return False

split = resource_id.split("/")

if (len(split) > 7):
print("Request id too long", file=sys.stderr)
return False

else:
print("Invalid Call",file=sys.stderr)
return False

return True
return False


def validation(resource_id, token, call):
if (not is_valid_token(token)):
print("Invalid Token", file=sys.stderr)
return Response(status=403)

if (token in token_cache):

token_expiry = datetime.datetime.strptime(token_cache[token]['expiry'], '%Y-%m-%dT%H:%M:%S.%fZ')

if (token_expiry < datetime.datetime.now()):
token_cache.pop(token)
else:
if (auth(token_cache[token], resource_id, call)):
symlink(resource_id)
return Response(status=200)
return Response(status=403)

body = {'token': token}

response = post(
url=config.INTROSPECT_URL,
headers={"content-type": "application/json"},
data=json.dumps(body),
cert=(config.RS_CERT_FILE, config.RS_KEY_FILE),
verify=False
)
if (response.status_code != 200):
return Response(status=response.status_code)
token_cache[token] = response.json()
if (auth(response.json(), resource_id, call)):
symlink(resource_id)
return Response(status=200)
return Response(status=403)

# TODO: Use Managed Cache library

@app.route('/api/on-hls-auth', methods=['GET'])
def on_hls_auth() -> Response:
"""
API to authenticate on_hls_subcription
:return:
API to authenticate on_hls_subcription:
input:
Request:
request.environ contains 'HTTP_URL' and 'HTTP_TOKEN'
'HTTP_URL': '/rtmp+hls/<encoded-resource-id>/index.m3u8'
'HTTP_TOKEN': '<encoded-token>'
return:
Response: status_code(200,403)
"""

if ('HTTP_URL' not in request.environ
or 'HTTP_TOKEN' not in request.environ
or len(request.environ['HTTP_TOKEN']) == 0):
print('Invalid Input', file=sys.stderr)
logging.info('Invalid Input', file=sys.stderr)
return Response(status=403)

uri = urlparse(request.environ['HTTP_URL'])
path_split = uri.path.split('/')

# TODO: Add detailed comments
if len(path_split) != 4:
print("Invalid ID", file=sys.stderr)
logging.info("Invalid ID", file=sys.stderr)
return Response(status=403)

resource_id = unquote_plus(path_split[2])
token = unquote_plus(request.environ['HTTP_TOKEN'])
call = 'play'
request_type = 'play'

return validation(resource_id, token, call)
return Response(status=200 if utils.validation(resource_id, token, request_type) else 400)


@app.route("/api/on-live-auth", methods=['POST'])
Expand All @@ -223,17 +56,24 @@ def on_live_auth() -> Response:
:return:
Response: status_code(200,403)
"""
if (not request or 'token' not in request.form or 'name' not in request.form or 'call' not in request.form):
return Response(status=403)

token = request.form['token']
resource_id = unquote_plus(request.form['name'])
call = request.form['call']
request_type = request.form['call']

return validation(resource_id, token, call)
return Response(status=200 if utils.validation(resource_id, token, request_type) else 400)


if __name__ == '__main__':
logging.basicConfig(level=logging.INFO)
src_path = config.RECORD_SRC_DIR
event_handler = Handler()
event_handler = utils.Handler()
observer = watchdog.observers.Observer()
observer.schedule(event_handler, path=src_path, recursive=True)
observer.schedule(event_handler, path=src_path, recursive=False)
observer.start()
app.run(threaded=True, port=3001, host='0.0.0.0')
app.run(threaded=True, port=3001, host='0.0.0.0', debug=True)

# TODO: ADD WSGI
# http_server = WSGIServer(('0.0.0.0', 3001), app, keyfile = 'ssl/privkey.pem', certfile='ssl/fullchain.pem')
11 changes: 5 additions & 6 deletions auth-backend/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,11 @@

MAX_SAFE_STRING_LEN = 512
AUTH_SERVER_NAME = os.environ["AUTH_SERVER"]
TOKEN_LEN = 16
TOKEN_LEN_HEX = 32
INTROSPECT_URL = "https://"+ AUTH_SERVER_NAME + "/auth/v1/token/introspect"
RS_CERT_FILE = "/root/auth-backend/resource-server.pem"
RS_CERT_FILE = "/root/auth-backend/resource-server.pem"
RS_KEY_FILE = "/root/auth-backend/resource-server.key.pem"
RECORD_SRC_DIR = '/root/datasetu-video-server/nginx/record/'
RECORD_TAR_DIR = '/root/datasetu-video-server/nginx/record'
HLS_SRC_DIR = '/root/datasetu-video-server/nginx/storage/rtmp+hls'

RECORD_SRC_DIR = '/root/datasetu-video-server/nginx/record'
RECORD_DEST_DIR = '/root/datasetu-video-server/nginx/record/'
HLS_SRC_DIR = '/root/datasetu-video-server/nginx/storage/rtmp+hls'
HLS_DEST_DIR = '/root/datasetu-video-server/nginx/storage/rtmp+hls/'
Loading

0 comments on commit d3fdfec

Please sign in to comment.