Exemple #1
0
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)
Exemple #2
0
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)
Exemple #3
0
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
Exemple #4
0
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')
Exemple #5
0
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
Exemple #6
0
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
Exemple #7
0
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
Exemple #8
0
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
Exemple #9
0
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
Exemple #10
0
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
Exemple #11
0
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
Exemple #12
0
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
Exemple #13
0
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
Exemple #14
0
def validateDomain(domain):
    idDomain = baseDomain(current_app.config['CLIENT_ID'], includeScheme=False)
    return domain == idDomain
Exemple #15
0
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)