def handleLogin(): current_app.logger.info('handleLogin [%s]' % request.method) me = None redirectURI = '%s/success' % current_app.config['BASEURL'] fromURI = request.args.get('from_uri') current_app.logger.info('redirectURI [%s] fromURI [%s]' % (redirectURI, fromURI)) form = LoginForm(me='', client_id=current_app.config['CLIENT_ID'], redirect_uri=redirectURI, from_uri=fromURI) if form.validate_on_submit(): current_app.logger.info('me [%s]' % form.me.data) me = 'https://%s/' % baseDomain(form.me.data, includeScheme=False) scope = '' authEndpoints = ninka.indieauth.discoverAuthEndpoints(me) if 'authorization_endpoint' in authEndpoints: authURL = None for url in authEndpoints['authorization_endpoint']: authURL = url break if authURL is not None: if me == current_app.config['BASEURL']: scope = 'post update delete' url = ParseResult(authURL.scheme, authURL.netloc, authURL.path, authURL.params, urllib.urlencode({ 'me': me, 'redirect_uri': form.redirect_uri.data, 'client_id': form.client_id.data, 'scope': scope, 'response_type': 'id' }), authURL.fragment).geturl() if current_app.dbRedis is not None: key = 'login-%s' % me data = current_app.dbRedis.hgetall(key) if data and 'token' in data: # clear any existing auth data current_app.dbRedis.delete('token-%s' % data['token']) current_app.dbRedis.hdel(key, 'token') current_app.dbRedis.hset(key, 'auth_url', ParseResult(authURL.scheme, authURL.netloc, authURL.path, '', '', '').geturl()) current_app.dbRedis.hset(key, 'from_uri', form.from_uri.data) current_app.dbRedis.hset(key, 'redirect_uri', form.redirect_uri.data) current_app.dbRedis.hset(key, 'client_id', form.client_id.data) current_app.dbRedis.hset(key, 'scope', scope) current_app.dbRedis.expire(key, current_app.config['AUTH_TIMEOUT']) # expire in N minutes unless successful current_app.logger.info('redirecting to [%s]' % url) return redirect(url) else: return 'insert fancy no auth endpoint found error message here', 403 templateContext = {} templateContext['title'] = 'Sign In' templateContext['form'] = form return render_template('login.jinja', **templateContext)
def handleLogin(): app.logger.info('handleLogin [%s]' % request.method) me = None redirectURI = '%s/success' % cfg.baseurl fromURI = request.args.get('from_uri') app.logger.info('redirectURI [%s] fromURI [%s]' % (redirectURI, fromURI)) form = LoginForm(me='', client_id=cfg.client_id, redirect_uri=redirectURI, from_uri=fromURI) if form.validate_on_submit(): app.logger.info('me [%s]' % form.me.data) me = 'https://%s/' % baseDomain(form.me.data, includeScheme=False) authEndpoints = ninka.indieauth.discoverAuthEndpoints(me) if 'authorization_endpoint' in authEndpoints: authURL = None for url in authEndpoints['authorization_endpoint']: authURL = url break if authURL is not None: url = ParseResult(authURL.scheme, authURL.netloc, authURL.path, authURL.params, urllib.urlencode({ 'me': me, 'redirect_uri': form.redirect_uri.data, 'client_id': form.client_id.data, 'scope': 'post', 'response_type': 'id' }), authURL.fragment).geturl() if db is not None: key = 'login-%s' % me data = db.hgetall(key) if data and 'token' in data: # clear any existing auth data db.delete('token-%s' % data['token']) db.hdel(key, 'token') db.hset(key, 'auth_url', ParseResult(authURL.scheme, authURL.netloc, authURL.path, '', '', '').geturl()) db.hset(key, 'from_uri', form.from_uri.data) db.hset(key, 'redirect_uri', form.redirect_uri.data) db.hset(key, 'client_id', form.client_id.data) db.hset(key, 'scope', 'post') db.expire(key, cfg.auth_timeout) # expire in N minutes unless successful app.logger.info('redirecting to [%s]' % url) return redirect(url) else: return 'insert fancy no auth endpoint found error message here', 403 templateContext = {} templateContext['title'] = 'Sign In' templateContext['form'] = form return render_template('login.jinja', **templateContext)
def handleMicroPub(): current_app.logger.info('handleMicroPub [%s]' % request.method) # form = MicroPubForm() access_token = request.headers.get('Authorization') if access_token: access_token = access_token.replace('Bearer ', '') me, client_id, scope = checkAccessToken(access_token) current_app.logger.info('[%s] [%s] [%s] [%s]' % (access_token, me, client_id, scope)) if me is None or client_id is None: return ('Access Token missing', 401, {}) else: if request.method == 'POST': domain = baseDomain(me, includeScheme=False) idDomain = baseDomain(current_app.config['CLIENT_ID'], includeScheme=False) if domain == idDomain and checkAccessToken(access_token): properties = {} for key in ('h', 'name', 'summary', 'content', 'published', 'updated', 'slug', 'location', 'syndication', 'syndicate-to', 'in-reply-to', 'repost-of', 'like-of', 'bookmark-of'): properties[key] = request.form.get(key) for key in request.form.keys(): if key.lower().startswith('mp-'): properties[key.lower()] = request.form.get(key) properties['category'] = request.form.getlist('category[]') properties['html'] = request.form.getlist('content[html]') for key in properties: current_app.logger.info(' %s = [%s]' % (key, properties[key])) data = { 'domain': domain, 'app': client_id, 'scope': scope, 'properties': properties } return micropub(request.method, data) else: return 'Unauthorized', 403 elif request.method == 'GET': # add support for /micropub?q=syndicate-to return 'not implemented', 501
def handleEmbed(): app.logger.info('handleEmbed') targetURL = request.args.get('url') responseFormat = request.args.get('format') maxWidth = request.args.get('maxwidth') maxHeight = request.args.get('maxheight') if responseFormat is None: responseFormat = 'json' responseFormat = responseFormat.lower() if targetURL is None: return 'invalid url', 404 else: siteCfg = Config() if os.path.exists(cfg.site_config): siteCfg.fromJson(cfg.site_config) url = urlparse(targetURL) targetRoute = url.path.replace(siteCfg.baseroute, '') if targetRoute.endswith('.html'): targetRoute = targetRoute[:-5] targetFile = os.path.join(siteCfg.paths.content, '%s.json' % targetRoute) print targetURL print url print targetRoute print targetFile # load all known mentions for the target if os.path.exists(targetFile): with open(targetFile, 'r') as h: post = json.load(h) thumbUrl, thumbWidth = findThumbnail(maxWidth, maxHeight) data = { "version": "1.0", "type": "rich", "author_name": post['author'], "author_url": siteCfg.baseurl, "provider_name": baseDomain(siteCfg.baseurl, includeScheme=False), "provider_url": siteCfg.baseurl, "title": post['title'], "thumbnail_url": '%s%s' % (siteCfg.baseurl, thumbUrl), "thumbnail_width": thumbWidth, "thumbnail_height": thumbWidth, "target": targetURL } data['html'] = _embed_html % data if responseFormat == 'json': return jsonify(data) else: return Response(_xml_response % data, mimetype='text/xml')
def handleMicroPub(): app.logger.info('handleMicroPub [%s]' % request.method) # form = MicroPubForm() access_token = request.headers.get('Authorization') if access_token: access_token = access_token.replace('Bearer ', '') me, client_id, scope = checkAccessToken(access_token) app.logger.info('[%s] [%s] [%s] [%s]' % (access_token, me, client_id, scope)) if me is None or client_id is None: return ('Access Token missing', 401, {}) else: if request.method == 'POST': domain = baseDomain(me, includeScheme=False) idDomain = baseDomain(cfg.client_id, includeScheme=False) if domain == idDomain and checkAccessToken(access_token): data = { 'event': 'create', 'domain': domain, 'baseurl': cfg.baseurl, 'baseroute': cfg.baseroute, 'app': client_id, 'scope': scope } for key in ('h', 'name', 'summary', 'content', 'published', 'updated', 'category', 'slug', 'location', 'syndication', 'syndicate-to', 'in-reply-to', 'repost-of', 'like-of'): data[key] = request.form.get(key) app.logger.info(' %s = [%s]' % (key, data[key])) for key in request.form.keys(): if key not in data: data[key] = request.form.get(key) app.logger.info(' %s = [%s]' % (key, data[key])) return micropub(data, db, app.logger, cfg.site_config) else: return 'Unauthorized', 403 elif request.method == 'GET': # add support for /micropub?q=syndicate-to return 'not implemented', 501
def process(sourceURL, targetURL): """Publish webmention for sourceURL Publishing is handled as a Webmention sent to the /publish URL as the targetURL included somewhere within the sourceURL All you need is to include the following: <a href="https://indieweb.news/publish"></a> somewhere in the sourceURL """ current_app.logger.info('process [%s][%s]' % (sourceURL, targetURL)) result = None mentions = ronkyuu.findMentions(sourceURL) for href in mentions['refs']: current_app.logger.info('process href [%s]' % href) if href != sourceURL and href == targetURL: utcdate = datetime.datetime.utcnow() tzLocal = pytz.timezone('America/New_York') timestamp = tzLocal.localize(utcdate, is_dst=None) postDate = timestamp.strftime('%Y-%m-%dT%H:%M:%S') domain = baseDomain(sourceURL, includeScheme=False) postID = str(uuid.uuid4()) data = { 'source': sourceURL, 'target': targetURL, 'created': postDate, 'updated': postDate, 'postid': postID, 'domain': domain, } event = { 'type': 'publish', 'key': postID, } r = current_app.palala.query('select * from domains where domain = "{domain}"'.format(**data)) if len(r) > 0: current_app.palala.run('update domains set updated = "{updated}" where domain = "{domain}"'.format(domain=domain, updated=postDate)) else: current_app.palala.run('insert into domains (domain, created, updated) values ("{domain}","{created}","{updated}");'.format(domain=domain,created=postDate,updated=postDate)) current_app.palala.run('insert into posts (postid, domain, source, target, created, updated) values ("{postid}","{domain}","{source}","{target}","{created}","{updated}")'.format(**data)) current_app.dbRedis.lpush('indienews-recent', postID) current_app.dbRedis.ltrim('indienews-recent', 0, 50) current_app.dbRedis.rpush('indienews-events', json.dumps(event)) result = data break return result
def loadConfig(configFilename, host=None, port=None, logpath=None): result = Config() result.fromJson(configFilename) if host is not None: result.host = host if port is not None: result.port = port if logpath is not None: result.paths.log = logpath if 'auth_timeout' not in result: result.auth_timeout = 300 if 'require_vouch' not in result: result.require_vouch = False if 'our_domain' not in result: result.our_domain = baseDomain(result.client_id, includeScheme=False) return result
def handleMedia(): # https://www.w3.org/TR/2016/CR-micropub-20160816/#media-endpoint current_app.logger.info('handleMedia [%s]' % request.method) me, client_id, scope, allowed = validateAccessToken(request.headers.get('Authorization')) if not allowed: return 'Not allowed', 401 domain = baseDomain(me, includeScheme=False) if request.method == 'POST' and validateDomain(domain): item = request.files.get('file') filename = secure_filename(item.filename) # TODO replace this with a plugin callable to retrieve what the file path and url should be item.save(os.path.join(current_app.config['MEDIA_FILES'], filename)) location = '%s%s%s/%s' % (current_app.config['BASEURL'], current_app.config['BASEROUTE'], current_app.config['MEDIA_DIR'], filename) return ('Media successful for %s' % location, 201, {'Location': location}) else: return 'Invalid request', 400
def mention(sourceURL, targetURL, vouchDomain=None, vouchRequired=False): """Process the Webmention of the targetURL from the sourceURL. """ # mentions = ronkyuu.findMentions(sourceURL) vouched = True if vouchRequired: vouched = processVouch(sourceURL, targetURL, vouchDomain) if vouched: utcdate = datetime.datetime.utcnow() tzLocal = pytz.timezone('America/New_York') timestamp = tzLocal.localize(utcdate, is_dst=None) domain = baseDomain(sourceURL, includeScheme=False) # mf2Data = Parser(doc=mentions['content']).to_dict() # hcard = extractHCard(mf2Data) data = { 'sourceURL': sourceURL, 'targetURL': targetURL, 'vouchDomain': vouchDomain, 'vouched': vouched and vouchRequired, 'postDate': timestamp.strftime('%Y-%m-%dT%H:%M:%S'), # 'hcard': hcard, # 'mf2data': mf2Data, } key = '%s/%s' % (domain, timestamp.strftime('%Y%m%d%H%M%S')) event = { 'type': 'webmention', 'key': key, } current_app.dbRedis.set(key, json.dumps(data)) current_app.dbRedis.rpush('palala-events', json.dumps(event)) response = jsonify(data) response.status_code = 201 response.headers['location'] = '/posts/%s' % key else: response = jsonify(data) response.status_code = 400 return response
def handleMicroPub(): current_app.logger.info('handleMicroPub [%s]' % request.method) # form = MicroPubForm() access_token = request.headers.get('Authorization') if access_token: access_token = access_token.replace('Bearer ', '') me, client_id, scope = checkAccessToken(access_token) current_app.logger.info('[%s] [%s] [%s] [%s]' % (access_token, me, client_id, scope)) if me is None or client_id is None: return ('Access Token missing', 401, {}) else: if request.method == 'POST': domain = baseDomain(me, includeScheme=False) idDomain = baseDomain(current_app.config['CLIENT_ID'], includeScheme=False) if domain == idDomain and checkAccessToken(access_token): properties = {} for key in ('h', 'name', 'summary', 'content', 'published', 'updated', 'slug', 'location', 'syndication', 'syndicate-to', 'in-reply-to', 'repost-of', 'like-of', 'bookmark-of'): properties[key] = request.form.get(key) for key in request.form.keys(): if key.lower().startswith('mp-'): properties[key.lower()] = request.form.get(key) properties['category'] = request.form.getlist('category[]') properties['html'] = request.form.getlist('content[html]') for key in properties: current_app.logger.info(' %s = [%s]' % (key, properties[key])) data = { 'domain': domain, 'app': client_id, 'scope': scope, 'properties': properties } return micropub(request.method, data) else: return 'Unauthorized', 403 elif request.method == 'GET': q = request.args.get('q') current_app.logger.info('GET q [%s]' % q) if q is not None and q.lower() == 'syndicate-to': if request_wants_json: resp = jsonify( {'syndicate-to': current_app.config['SITE_SYNDICATE']}) resp.status_code = 200 return resp else: return ('&'.join( map( 'syndicate-to[]={0}'.format, map(urllib.quote, current_app.config['SITE_SYNDICATE']))), 200, { 'Content-Type': 'application/x-www-form-urlencoded' }) else: return 'not implemented', 501 else: return 'not implemented', 501
def handleAccessToken(): current_app.logger.info('handleAccessToken [%s]' % request.method) form = MPTokenForm(me=current_app.config['BASEURL'], client_id=current_app.config['CLIENT_ID'], redirect_uri='%s/access' % current_app.config['BASEURL'], from_uri='%s/access' % current_app.config['BASEURL'], scope='post') if form.validate_on_submit(): me = 'https://%s/' % baseDomain(form.me.data, includeScheme=False) authEndpoints = ninka.indieauth.discoverAuthEndpoints(me) if 'authorization_endpoint' in authEndpoints: authURL = None for url in authEndpoints['authorization_endpoint']: authURL = url break if authURL is not None: url = ParseResult( authURL.scheme, authURL.netloc, authURL.path, authURL.params, urllib.urlencode({ 'me': me, 'redirect_uri': form.redirect_uri.data, 'client_id': form.client_id.data, 'scope': form.scope.data, 'response_type': 'id' }), authURL.fragment).geturl() key = 'access-%s' % me data = current_app.dbRedis.hgetall(key) if data and 'token' in data: # clear any existing auth data current_app.dbRedis.delete('token-%s' % data['token']) current_app.dbRedis.hdel(key, 'token') current_app.dbRedis.hset( key, 'auth_url', ParseResult(authURL.scheme, authURL.netloc, authURL.path, '', '', '').geturl()) current_app.dbRedis.hset(key, 'redirect_uri', form.redirect_uri.data) current_app.dbRedis.hset(key, 'client_id', form.client_id.data) current_app.dbRedis.hset(key, 'scope', form.scope.data) current_app.dbRedis.expire( key, current_app.config['AUTH_TIMEOUT'] ) # expire in N minutes unless successful current_app.logger.info('redirecting to [%s]' % url) return redirect(url) else: return 'insert fancy no auth endpoint found error message here', 403 else: me = request.args.get('me') code = request.args.get('code') current_app.logger.info('me [%s] code [%s]' % (me, code)) if code is None: templateContext = {} templateContext['form'] = form return render_template('mptoken.jinja', **templateContext) else: current_app.logger.info('getting data to validate auth code') key = 'access-%s' % me data = current_app.dbRedis.hgetall(key) if data: current_app.logger.info('calling [%s] to validate code' % data['auth_url']) r = ninka.indieauth.validateAuthCode( code=code, client_id=data['client_id'], redirect_uri=data['redirect_uri'], validationEndpoint=data['auth_url']) current_app.logger.info('validateAuthCode returned %s' % r['status']) if r['status'] == requests.codes.ok: current_app.logger.info('login code verified') token = str(uuid.uuid4()) current_app.dbRedis.hset(key, 'code', code) current_app.dbRedis.hset(key, 'token', token) current_app.dbRedis.expire( key, current_app.config['AUTH_TIMEOUT']) current_app.dbRedis.set('token-%s' % token, key) current_app.dbRedis.expire( 'token-%s' % code, current_app.config['AUTH_TIMEOUT']) return 'Access Token: %s' % token, 200 else: current_app.logger.info('login invalid') clearAuth() return 'Invalid', 401 else: current_app.logger.info('nothing found for [%s]' % me) clearAuth() return 'Invalid', 401
def handleMicroPub(): current_app.logger.info('handleMicroPub [%s]' % request.method) me, client_id, scope, allowed = validateAccessToken(request.headers.get('Authorization')) if not allowed: return 'Not allowed', 401 domain = baseDomain(me, includeScheme=False) if request.method == 'POST' and validateDomain(domain): data = { 'domain': domain, 'app': client_id, 'scope': scope, } payload = request.get_json() properties = { 'type': 'h-entry', 'action': 'create', } for _key in _property_keys: properties[_key] = None for _key in ('content', 'html', 'category', 'photo', 'photo_files'): properties[_key] = [] for key in ('photo', 'photo[]'): items = request.files.getlist(key) for item in items: filename = secure_filename(item.filename) properties['photo_files'].append(filename) # TODO rectify why we save files here to uploads dir but media endpoint doesn't item.save(os.path.join(current_app.config['UPLOADS'], filename)) # form encoded if payload is None: properties['type'] = 'h-%s' % request.form.get('h') for key, value in request.form.iteritems(multi=True): key = key.lower() if key == 'category': properties[key].append(value) elif key == 'photo': properties[key].append((request.form.get(key), '')) elif key == 'content': properties[key] = request.form.get(key).replace('\r\n', '\n').split('\n') else: properties[key] = value current_app.logger.info(' %s --> %s' % (key, value)) # get any photos-as-array values properties['photo'] += list(zip(request.form.getlist('photo[value]'), request.form.getlist('photo[alt]'))) # get any categorties as array values for key, value in request.form.iteritems(multi=True): if key.lower().startswith('category['): properties['category'].append(value) properties['html'] = request.form.getlist('content[html]') else: # json data if 'type' in payload: properties['type'] = payload['type'][0] if 'action' in payload: for key in ('action', 'url', 'replace', 'add', 'delete'): if key in payload: properties[key] = payload[key] if 'properties' in payload: for key in payload['properties']: value = payload['properties'][key] properties[key] = value current_app.logger.info(' %s ==> %s' % (key, value)) if type(properties['content']) is dict: if 'html' in properties['content']: properties['html'] = properties['content']['html'] properties['content'] = [] if 'photo' in properties: photos = [] for item in properties['photo']: alt = '' if type(item) is dict: photo = item['value'] if 'alt' in item: alt = item['alt'] else: photo = item photos.append((photo, alt)) properties['photo'] = photos data['properties'] = properties for key in data['properties']: current_app.logger.info(' %s = %s' % (key, data['properties'][key])) return micropub(request.method, data) elif request.method == 'GET': # https://www.w3.org/TR/2016/CR-micropub-20160816/#querying query = request.args.get('q') current_app.logger.info('query [%s]' % query) if query is not None: query = query.lower() respJson = { 'media-endpoint': current_app.config['MEDIA_ENDPOINT'], 'syndicate-to': current_app.config['SITE_SYNDICATE'] } respParam = '&'.join(map('syndicate-to[]={0}'.format, map(urllib.quote, current_app.config['SITE_SYNDICATE']))) respParam += '&media-endpoint=%s' % (current_app.config['MEDIA_ENDPOINT']) if query == 'config': # https://www.w3.org/TR/2016/CR-micropub-20160816/#configuration if request_wants_json: resp = jsonify(respJson) resp.status_code = 200 return resp else: return (respParam, 200, {'Content-Type': 'application/x-www-form-urlencoded'}) elif query == 'syndicate-to': # https://www.w3.org/TR/2016/CR-micropub-20160816/#syndication-targets if request_wants_json: resp = jsonify(respJson) resp.status_code = 200 return resp else: return (respParam, 200, {'Content-Type': 'application/x-www-form-urlencoded'}) elif query == 'source': # https://www.w3.org/TR/2016/CR-micropub-20160816/#source-content url = request.args.get('url') properties = [] for key in ('properties', 'properties[]'): item = request.args.getlist(key) if len(item) > 0: properties += item current_app.logger.info('url: %s properties: %d %s' % (url, len(properties), properties)) # "If no properties are specified, then the response must include all properties, # as well as a type property indicating the vocabulary of the post." # so this is a list of properties the code currently handles if len(properties) == 0: properties = ['type', 'category', 'content', 'published'] targetPath = urlparse(url).path pathItems = targetPath.split('.') current_app.logger.info('[%s] %s' % (targetPath, pathItems)) # normalize the url target to remove any extension if pathItems[-1].lower() == 'html': targetPath = '.'.join(pathItems[:-1]) slug = targetPath.replace(current_app.config['BASEROUTE'], '') targetFile = '%s.json' % os.path.join(current_app.config['SITE_CONTENT'], slug) current_app.logger.info('targetFile: %s' % targetFile) if os.path.exists(targetFile): with open(targetFile, 'r') as h: post = json.load(h) respJson = { "type": ["h-entry"], "properties": {} } if 'published' in properties: respJson['properties']['published'] = [ post['published'] ] if 'category' in properties and len(post['tags']) > 0: respJson['properties']['category'] = post['tags'].split(',') if 'content' in properties: respJson['properties']['content'] = post['content'].split('\n') current_app.logger.info(json.dumps(respJson)) resp = jsonify(respJson) resp.status_code = 200 return resp else: return 'not found', 404 else: return 'not implemented', 501 else: return 'not implemented', 501 else: return 'not implemented', 501
def handleAccessToken(): current_app.logger.info('handleAccessToken [%s]' % request.method) form = MPTokenForm(me=current_app.config['BASEURL'], client_id=current_app.config['CLIENT_ID'], redirect_uri='%s/access' % current_app.config['BASEURL'], from_uri='%s/access' % current_app.config['BASEURL'], scope='post') if form.validate_on_submit(): me = 'https://%s/' % baseDomain(form.me.data, includeScheme=False) authEndpoints = ninka.indieauth.discoverAuthEndpoints(me) if 'authorization_endpoint' in authEndpoints: authURL = None for url in authEndpoints['authorization_endpoint']: authURL = url break if authURL is not None: url = ParseResult(authURL.scheme, authURL.netloc, authURL.path, authURL.params, urllib.urlencode({ 'me': me, 'redirect_uri': form.redirect_uri.data, 'client_id': form.client_id.data, 'scope': form.scope.data, 'response_type': 'id' }), authURL.fragment).geturl() key = 'access-%s' % me data = current_app.dbRedis.hgetall(key) if data and 'token' in data: # clear any existing auth data current_app.dbRedis.delete('token-%s' % data['token']) current_app.dbRedis.hdel(key, 'token') current_app.dbRedis.hset(key, 'auth_url', ParseResult(authURL.scheme, authURL.netloc, authURL.path, '', '', '').geturl()) current_app.dbRedis.hset(key, 'redirect_uri', form.redirect_uri.data) current_app.dbRedis.hset(key, 'client_id', form.client_id.data) current_app.dbRedis.hset(key, 'scope', form.scope.data) current_app.dbRedis.expire(key, current_app.config['AUTH_TIMEOUT']) # expire in N minutes unless successful current_app.logger.info('redirecting to [%s]' % url) return redirect(url) else: return 'insert fancy no auth endpoint found error message here', 403 else: me = request.args.get('me') code = request.args.get('code') current_app.logger.info('me [%s] code [%s]' % (me, code)) if code is None: templateContext = {} templateContext['form'] = form return render_template('mptoken.jinja', **templateContext) else: current_app.logger.info('getting data to validate auth code') key = 'access-%s' % me data = current_app.dbRedis.hgetall(key) if data: current_app.logger.info('calling [%s] to validate code' % data['auth_url']) r = ninka.indieauth.validateAuthCode(code=code, client_id=data['client_id'], redirect_uri=data['redirect_uri'], validationEndpoint=data['auth_url']) current_app.logger.info('validateAuthCode returned %s' % r['status']) if r['status'] == requests.codes.ok: current_app.logger.info('login code verified') token = str(uuid.uuid4()) current_app.dbRedis.hset(key, 'code', code) current_app.dbRedis.hset(key, 'token', token) current_app.dbRedis.expire(key, current_app.config['AUTH_TIMEOUT']) current_app.dbRedis.set('token-%s' % token, 'app-%s-%s-%s' % (me, data['client_id'], data['scope'])) current_app.dbRedis.expire('token-%s' % token, current_app.config['AUTH_TIMEOUT']) app_key = 'app-%s-%s-%s' % (me, data['client_id'], data['scope']) current_app.dbRedis.hset(app_key, 'auth_url', data['auth_url']) current_app.dbRedis.hset(app_key, 'redirect_uri', data['redirect_uri']) current_app.dbRedis.hset(app_key, 'client_id', data['client_id']) current_app.dbRedis.hset(app_key, 'scope', data['scope']) current_app.dbRedis.expire(app_key, current_app.config['AUTH_TIMEOUT']) # expire in N minutes unless successful return 'Access Token: %s' % token, 200 else: current_app.logger.info('login invalid') clearAuth() return 'Invalid', 401 else: current_app.logger.info('nothing found for [%s]' % me) clearAuth() return 'Invalid', 401
def validateDomain(domain): idDomain = baseDomain(current_app.config['CLIENT_ID'], includeScheme=False) return domain == idDomain
def handleLogin(): current_app.logger.info("handleLogin [%s]" % request.method) me = None redirectURI = "%s/success" % current_app.config["BASEURL"] fromURI = request.args.get("from_uri") current_app.logger.info("redirectURI [%s] fromURI [%s]" % (redirectURI, fromURI)) form = LoginForm(me="", client_id=current_app.config["CLIENT_ID"], redirect_uri=redirectURI, from_uri=fromURI) if form.validate_on_submit(): current_app.logger.info("me [%s]" % form.me.data) me = "https://%s/" % baseDomain(form.me.data, includeScheme=False) scope = "" authEndpoints = ninka.indieauth.discoverAuthEndpoints(me) if "authorization_endpoint" in authEndpoints: authURL = None for url in authEndpoints["authorization_endpoint"]: authURL = url break if authURL is not None: if me == current_app.config["BASEURL"]: scope = "post update delete" url = ParseResult( authURL.scheme, authURL.netloc, authURL.path, authURL.params, urllib.urlencode( { "me": me, "redirect_uri": form.redirect_uri.data, "client_id": form.client_id.data, "scope": scope, "response_type": "id", } ), authURL.fragment, ).geturl() if current_app.dbRedis is not None: key = "login-%s" % me data = current_app.dbRedis.hgetall(key) if data and "token" in data: # clear any existing auth data current_app.dbRedis.delete("token-%s" % data["token"]) current_app.dbRedis.hdel(key, "token") current_app.dbRedis.hset( key, "auth_url", ParseResult(authURL.scheme, authURL.netloc, authURL.path, "", "", "").geturl() ) current_app.dbRedis.hset(key, "from_uri", form.from_uri.data) current_app.dbRedis.hset(key, "redirect_uri", form.redirect_uri.data) current_app.dbRedis.hset(key, "client_id", form.client_id.data) current_app.dbRedis.hset(key, "scope", scope) current_app.dbRedis.expire( key, current_app.config["AUTH_TIMEOUT"] ) # expire in N minutes unless successful current_app.logger.info("redirecting to [%s]" % url) return redirect(url) else: return "insert fancy no auth endpoint found error message here", 403 templateContext = {} templateContext["title"] = "Sign In" templateContext["form"] = form return render_template("login.jinja", **templateContext)