def handleToken(): current_app.logger.info('handleToken [%s]' % request.method) if request.method == 'GET': me, client_id, scope, allowed = validateAccessToken( request.headers.get('Authorization')) if not allowed: return 'Not allowed', 401 params = { 'me': me, 'client_id': client_id, } if scope is not None: params['scope'] = scope return (urllib.urlencode(params), 200, { 'Content-Type': 'application/x-www-form-urlencoded' }) elif request.method == 'POST': code = request.form.get('code') me = request.form.get('me') redirect_uri = request.form.get('redirect_uri') client_id = request.form.get('client_id') state = request.form.get('state') current_app.logger.info(' code [%s]' % code) current_app.logger.info(' me [%s]' % me) current_app.logger.info(' client_id [%s]' % client_id) current_app.logger.info(' state [%s]' % state) current_app.logger.info(' redirect_uri [%s]' % redirect_uri) r = ninka.indieauth.validateAuthCode(code=code, client_id=me, state=state, redirect_uri=redirect_uri) if r['status'] == requests.codes.ok: current_app.logger.info('token request auth code verified') scope = r['response']['scope'] key = 'app-%s-%s-%s' % (me, client_id, scope) token = current_app.dbRedis.get(key) if token is None: token = str(uuid.uuid4()) token_key = 'token-%s' % token current_app.dbRedis.set(key, token) current_app.dbRedis.set(token_key, key) current_app.logger.info(' token generated for [%s] : [%s]' % (key, token)) params = {'me': me, 'scope': scope, 'access_token': token} return (urllib.urlencode(params), 200, { 'Content-Type': 'application/x-www-form-urlencoded' })
def handleToken(): current_app.logger.info('handleToken [%s]' % request.method) if request.method == 'GET': me, client_id, scope, allowed = validateAccessToken(request.headers.get('Authorization')) if not allowed: return 'Not allowed', 401 params = { 'me': me, 'client_id': client_id, } if scope is not None: params['scope'] = scope return (urllib.urlencode(params), 200, {'Content-Type': 'application/x-www-form-urlencoded'}) elif request.method == 'POST': code = request.form.get('code') me = request.form.get('me') redirect_uri = request.form.get('redirect_uri') client_id = request.form.get('client_id') state = request.form.get('state') current_app.logger.info(' code [%s]' % code) current_app.logger.info(' me [%s]' % me) current_app.logger.info(' client_id [%s]' % client_id) current_app.logger.info(' state [%s]' % state) current_app.logger.info(' redirect_uri [%s]' % redirect_uri) r = ninka.indieauth.validateAuthCode(code=code, client_id=me, state=state, redirect_uri=redirect_uri) if r['status'] == requests.codes.ok: current_app.logger.info('token request auth code verified') scope = r['response']['scope'] key = 'app-%s-%s-%s' % (me, client_id, scope) token = current_app.dbRedis.get(key) if token is None: token = str(uuid.uuid4()) token_key = 'token-%s' % token current_app.dbRedis.set(key, token) current_app.dbRedis.set(token_key, key) current_app.logger.info(' token generated for [%s] : [%s]' % (key, token)) params = { 'me': me, 'scope': scope, 'access_token': token } return (urllib.urlencode(params), 200, {'Content-Type': 'application/x-www-form-urlencoded'})
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 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 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 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