def get_site_settings_values(): sql = f''' select site_visibility, title, fontsize, selfurl, coloraccent, isdarkmode, description, copyright, websiteurl, brandmail, brandlogourl, faviconurl, appiconurl from settings.site_settings ''' msg = {'body': {'action': 'run', 'queries': [sql]}} method, response = db_handler(msg) if method == 'ok': response_dict = response[0][1:-1] return ok(json.loads(response_dict)) else: return internal_server_error()
def handler(event, context) -> dict: del context # unused try: refresh_token = get_refresh_token(event) except NotLoggedIn: return { 'statusCode': 401, 'body': "Not logged in", } except BadRequest as e: return bad_request('', e) except InternalServerError as e: return internal_server_error('', e) if 'domains' in refresh_token: # delegated token with domain restrictions domains = refresh_token['domains'] else: domains = get_domains() access_tokens = {} try: for domain in domains: access_tokens[domain] = access_token_from_refresh_token( refresh_token, domain, ) except BadRequest as e: return bad_request('', e) return { 'statusCode': 200, 'headers': { 'Content-Type': 'application/json', }, 'body': json.dumps(access_tokens), }
def handler(event, context) -> dict: del context # unused try: redirect_uri = event['queryStringParameters']['redirect_uri'] except KeyError: return bad_request('', "No redirect_uri parameter found") redirect_uri_comp = urlsplit(redirect_uri) try: refresh_token = get_refresh_token(event) except NotLoggedIn: state = { 'action': 'authorize', 'redirect_uri': redirect_uri, } raw_state = jwt.encode( state, get_state_jwt_secret(), algorithm='HS256', ) return redirect_to_cognito(state=raw_state) except BadRequest as e: return bad_request('', e) except InternalServerError as e: return internal_server_error('', e) # Is this domain allowed? if not is_allowed_domain(redirect_uri_comp.netloc): return bad_request('', f"{redirect_uri} is not an allowed domain") if 'domains' in refresh_token: # delegated token with domain restrictions if redirect_uri_comp.netloc not in refresh_token['domains']: return bad_request( '', f"{redirect_uri} is not an allowed domain for this refresh token" ) try: access_token = access_token_from_refresh_token( refresh_token, redirect_uri_comp.netloc) except BadRequest as e: return bad_request('', e) return { 'statusCode': 302, 'headers': { 'Content-Type': 'text/plain', 'Location': urlunsplit(( 'https', redirect_uri_comp.netloc, get_config().set_cookie_path, urlencode({ # query 'access_token': access_token, # Key must match with λ@E's expectations 'redirect_uri': redirect_uri, # Key must match with λ@E's expectations }), '', # fragment )), }, 'body': 'Redirecting...', }
def handler(event, context) -> dict: del context # unused try: refresh_token = get_refresh_token(event) except NotLoggedIn: state = { 'action': 'delegate', } raw_state = jwt.encode( state, get_state_jwt_secret(), algorithm='HS256', ) return redirect_to_cognito(state=raw_state) except BadRequest as e: return bad_request('', e) except InternalServerError as e: return internal_server_error('', e) if event['httpMethod'] == 'GET': structlog.get_logger().msg("Rendering index HTML") if 'domains' in refresh_token: # User wants to further narrow his access domains = refresh_token['domains'] groups = {} else: domains = get_domains() groups = {} scan_paginator = dynamodb_client.get_paginator('scan') response_iterator = scan_paginator.paginate( TableName=get_config().group_table, ) for page in response_iterator: for group_entry in page['Items']: try: groups[group_entry['group'] ['S']] = group_entry['domains']['SS'] except KeyError as e: structlog.get_logger().msg( "Invalid group in DynamoDB: " + repr(group_entry)) pass with open(os.path.join(os.path.dirname(__file__), 'delegate.html')) as f: html = f.read() html = html.replace('{{{domains}}}', json.dumps(domains)) \ .replace('{{{groups}}}', json.dumps(groups)) \ .replace('{{{use_grant_url}}}', json.dumps(f"https://{os.environ['DOMAIN_NAME']}/use_grant?grant=")) return { 'statusCode': 200, 'headers': { 'Content-Type': 'text/html', }, 'body': html, } elif event['httpMethod'] == 'POST': structlog.get_logger().log("Validating POST request", body=event['body']) values = urllib.parse.parse_qs(event['body'], strict_parsing=True) structlog.get_logger().log("Decoded body", body=values) try: exp = int(values['exp'][0]) del values['exp'] subject = values['subject'][0] assert len(subject) > 0 del values['subject'] except (KeyError, AssertionError): return bad_request('mandatory fields not present') if exp > refresh_token['exp']: return bad_request('expiration too long') domains = set(values.keys()) for domain in domains: if not re.match(r'^[a-zA-Z0-9.-]+$', domain): return bad_request( '', f"`{domain}` does not look like a domain name") if not is_allowed_domain(domain): return bad_request('', 'Unknown domain in request') if 'domains' in refresh_token: if not domains.issubset(refresh_token['domains']): return bad_request('', 'domain requested outside refresh_token') delegate_token = { 'iat': int(time.time()), 'exp': exp, 'domains': list(domains), 'azp': refresh_token['azp'], # Authorized Party 'sub': refresh_token.get('sub', []) + [subject], # subject } structlog.get_logger().log("Issuing JWT", jwt=delegate_token) raw_delegate_token = jwt.encode( delegate_token, get_grant_jwt_secret(), algorithm='HS256', ).decode('ascii') return { 'statusCode': 200, 'headers': { 'Content-Type': 'text/plain', }, 'body': raw_delegate_token, }
def handler(event, context) -> dict: del context # unused try: cognito_code = event['queryStringParameters']['code'] state = event['queryStringParameters']['state'] except (TypeError, KeyError): return bad_request('', 'missing required parameter') try: state = jwt.decode( state, get_state_jwt_secret(), algorithms=['HS256'], ) except jwt.InvalidTokenError: return bad_request('', 'invalid state token') try: cognito_token = exchange_cognito_code(event, cognito_code) except BadRequest: return bad_request() except InternalServerError: return internal_server_error() # Issue a token valid for 180 days. This allows the user to issue delegate # tokens for up to this time. # But set the expiration of the Cookie itself to the validity of the # Cognito token. # Unless the user actively safeguards his cookie, he will have to # re-authenticate with Cognito. If this is malicious intend, the user # could delegate the same access to himself, and get the same result. now = int(time.time()) refresh_token = { 'iat': now, # Issued AT 'exp': now + 180 * 24 * 60 * 60, # EXPire: 180 days, maximum duration of delegated tokens 'azp': cognito_token['cognito:username'], # AuthoriZed Party } raw_refresh_token = jwt.encode( refresh_token, get_refresh_token_jwt_secret(), algorithm='HS256', ).decode('ascii') structlog.get_logger().msg( "Cognito Code exchanged succesfully, issuing refresh_token", refresh_token=refresh_token) # Don't log signed token, only payload try: if state['action'] == 'index': location = f"https://{os.environ['DOMAIN_NAME']}/" elif state['action'] == 'delegate': location = f"https://{os.environ['DOMAIN_NAME']}/delegate" elif state['action'] == 'authorize': location = f"https://{os.environ['DOMAIN_NAME']}/authorize?" + \ f"redirect_uri={urllib.parse.quote_plus(state['redirect_uri'])}" else: raise ValueError(f"Invalid action `{state['action']}`") except (KeyError, ValueError) as e: structlog.get_logger().msg("state is invalid", exception=e) return internal_server_error() return { 'statusCode': 302, 'headers': { 'Content-Type': 'text/plain', 'Location': location, 'Set-Cookie': generate_cookie(get_config().cookie_name_refresh_token, raw_refresh_token, max_age=int(cognito_token['exp'] - now)), }, 'body': 'Redirecting...', }
def handler(event, context) -> dict: del context # unused raw_refresh_token = None refresh_token_exp = None domains = None azp = None sub = [] try: raw_refresh_token = get_raw_refresh_token(event) refresh_token = parse_raw_refresh_token(raw_refresh_token) refresh_token_exp = refresh_token['exp'] azp = refresh_token['azp'] # Mandatory sub = refresh_token.get('sub', []) # optional try: domains = refresh_token['domains'] except KeyError: pass except (NotLoggedIn, BadRequest): pass except InternalServerError as e: return internal_server_error( 'Something went wrong parsing the refresh token', e) state = { 'action': 'index', } raw_state = jwt.encode( state, get_state_jwt_secret(), algorithm='HS256', ) now = time.time() csrf = jwt.encode( { 'iat': now, 'sub': raw_refresh_token, }, get_csrf_jwt_secret(), algorithm='HS256', ).decode('utf-8') with open(os.path.join(os.path.dirname(__file__), 'index.html')) as f: html = f.read() html = html.replace('{{{authenticate}}}', cognito_url(raw_state)) \ .replace('{{{refresh_token_exp}}}', json.dumps(refresh_token_exp)) \ .replace('{{{domains}}}', json.dumps(domains)) \ .replace('{{{azp}}}', json.dumps(azp)) \ .replace('{{{sub}}}', json.dumps(sub)) \ .replace('{{{csrf}}}', csrf) return { 'statusCode': 200, 'headers': { 'Content-Type': 'text/html', }, 'body': html, }