示例#1
0
def upload():
    f = request.files.get('file', request.form.get('file'))
    if f is None:
        return make_response('File not uploaded', 403)
    filename = request.form.get('fname', f.filename)
    if filename is None:
        return make_response('File name not specified', 403)
    esecs = int(request.form.get('expires', config['default-expires']))
    d_now = datetime.now()
    expires = (d_now + timedelta(seconds=esecs)).replace(
        tzinfo=pytz.timezone(time.tzname[0]))
    data = f.stream.read()
    sha256sum_gen = sha256()
    sha256sum_gen.update(data)
    sha256sum = sha256sum_gen.hexdigest()
    received_sha256sum = request.form.get('sha256sum')
    if received_sha256sum and received_sha256sum != sha256sum:
        return make_response('Checksum does not match', 422)
    file_id = cr.gen_random_str(16)
    filename = os.path.basename(filename)
    oneshot = val_to_boolean(request.form.get('oneshot', False))
    store_as_raw = val_to_boolean(request.form.get('raw', False))
    if store_as_raw:
        contents = data
        file_key = '-'
    else:
        file_key = cr.gen_random_str(16)
        engine = cr.Rioja(file_key, bits=256)
        contents = engine.encrypt(data, b64=False)
    location = (f'{EXTERNAL_URL}/d/{file_id}/' f'{file_key}/{filename}')
    response = make_response(dict(url=location), 201)
    response.headers['Location'] = location
    if EXTERNAL_URL:
        response.autocorrect_location_header = False
    response.headers['Cache-Control'] = ('no-cache, no-store, must-revalidate,'
                                         ' post-check=0, pre-check=0')
    response.headers['Pragma'] = 'no-cache'
    response.headers['Expires'] = expires.isoformat() + 'Z'
    mimetype = mimetypes.guess_type(filename)[0]
    if mimetype is None:
        mimetype = 'application/octet-stream'
    if mimetype == 'application/octet-stream':
        try:
            data.decode()
            mimetype = 'text/plain'
        except:
            pass
    db.query('stor.add',
             id=file_id,
             fname=filename,
             sha256sum=sha256sum,
             mimetype=mimetype,
             d=d_now,
             expires=expires,
             oneshot=oneshot,
             data=contents)
    return response
示例#2
0
def _handle_user_auth(user_info, provider):
    user_id = get_user_id()
    try:
        user = _d.db.qlookup('user.oauth.get',
                             sub=user_info.sub,
                             provider=provider)
        if user_id and user['id'] != user_id:
            raise ResourceAlreadyExists
        else:
            user_id = user['id']
            _d.db.query('user.provider.update.name',
                        id=user_id,
                        provider=provider,
                        sub=user_info.sub,
                        name=_get_oauth_user_name(user_info, provider))
    except LookupError:
        if not user_id:
            if allow_registration:
                user_id = _d.db.qcreate('user.create.empty',
                                        api_key=gen_random_str(),
                                        d_created=datetime.datetime.now())
            else:
                raise AccessDenied
        _d.db.query('user.provider.create',
                    id=user_id,
                    provider=provider,
                    sub=user_info.sub,
                    name=_get_oauth_user_name(user_info, provider))
        _log_user_event('account.register', user_id=user_id)
        _call_handler('account.register', user_id=user_id, user_info=user_info)
    return user_id
示例#3
0
def regenerate_user_api_key():
    """
    Generate new API key for user

    Returns:
        new API key
    Raises:
        LookupError: user not found
        webauth.AccessDenied: user is not logged in
    """
    user_id = get_user_id()
    if user_id:
        k = gen_random_str()
        _d.db.query('user.api_key.set', _cr=True, id=user_id, api_key=k)
        _log_user_event('api_key.change')
        return k
    else:
        raise AccessDenied
示例#4
0
def register(email,
             password,
             confirmed=True,
             next_action_uri=None,
             _invoke_handler=True):
    """
    Register new user in traditional way

    Args:
        email: user email
        password: user password
        confirmed: if no, email confirmation is required (sent automatically)
        next_action_uri: redirect URi after email confirmation
    Raises:
        webauth.ResourceAlreadyExists: email already registered
    """
    if allow_registration:
        try:
            user_id = _d.db.qcreate('user.create',
                                    email=email,
                                    password=sha256(
                                        password.encode()).hexdigest(),
                                    api_key=gen_random_str(),
                                    d_created=datetime.datetime.now(),
                                    confirmed=confirmed)
        except sqlalchemy.exc.IntegrityError as e:
            raise ResourceAlreadyExists(e)
        session[f'{_d.x_prefix}user_id'] = user_id
        session[f'{_d.x_prefix}user_confirmed'] = confirmed
        _log_user_event('account.register', user_id=user_id)
        if _invoke_handler:
            _call_handler('account.register', user_id=user_id)
        if not confirmed:
            confirm_email_ownership(user_id=user_id,
                                    email=email,
                                    next_action_uri=next_action_uri)

        return redirect(_next_uri())
    else:
        raise AccessDenied
示例#5
0
def init(app,
         db,
         config,
         base_prefix='/auth',
         root_uri='/',
         providers=['google', 'facebook', 'github'],
         smtp=None,
         fix_ssl=True):
    """
    Initalize framework

    Args:
        app: Flask app
        db: pyaltt2.db.Database object
        config: configuration dict
        base_prefix: base prefix for auth urls
        root_uri: default next uri
        providers: oauth2 providers list
        smtp: pyaltt2.mail.SMTP object, required if email confirmations are
        used
        fix_ssl: force SSL everywhere (True by default)
    """

    if not app.config.get('SECRET_KEY'):
        app.config['SECRET_KEY'] = gen_random_str()

    _d.x_prefix, _d.dot_prefix = _format_prefix(base_prefix)
    _d.db = db.clone(rq_func=rq)
    _d.kv = KVStorage(db=db, table_name='webauth_kv')
    _d.root_uri = root_uri
    _d.smtp = smtp

    if fix_ssl:
        from werkzeug.middleware.proxy_fix import ProxyFix
        app.wsgi_app = ProxyFix(app.wsgi_app, x_proto=1)

    def init_db():
        from sqlalchemy import (MetaData, Table, Column, BigInteger, VARCHAR,
                                JSON, CHAR, DateTime, ForeignKey, Index,
                                Boolean, Numeric)
        meta = MetaData()
        user = Table(
            'webauth_user', meta,
            Column('id', BigInteger(), primary_key=True, autoincrement=True),
            Column('email', VARCHAR(255), nullable=True, unique=True),
            Column('password', CHAR(64), nullable=True),
            Column('api_key', CHAR(32), nullable=True, unique=True),
            Index('webauth_user_api_key', 'api_key'),
            Column('d_created', DateTime(timezone=True), nullable=False),
            Column('d_active', DateTime(timezone=True), nullable=True),
            Column('confirmed', Boolean, nullable=False, server_default='0'),
            Column('otp', Numeric(1, 0), nullable=False, server_default='0'),
            Column('otp_secret', CHAR(16), nullable=True))
        user_auth = Table(
            f'webauth_user_auth', meta,
            Column('id', BigInteger(), primary_key=True, autoincrement=True),
            Column('user_id',
                   BigInteger(),
                   ForeignKey('webauth_user.id', ondelete='CASCADE'),
                   nullable=False),
            Index('webauth_user_auth_user_id', 'user_id'),
            Column('provider', VARCHAR(15), nullable=False),
            Column('sub', VARCHAR(255), nullable=False),
            Column('name', VARCHAR(255), nullable=True),
            Index('webauth_user_auth_sub_provider',
                  'sub',
                  'provider',
                  unique=True))
        user_log = Table(
            f'webauth_user_log', meta,
            Column('id', BigInteger(), primary_key=True, autoincrement=True),
            Column('user_id', BigInteger(), nullable=False),
            Index('webauth_user_log_user_id', 'user_id'),
            Column('d', DateTime(timezone=True), nullable=False),
            Column('event', VARCHAR(1024), nullable=False),
            Column('ip', VARCHAR(45), nullable=False))
        meta.create_all(db.connect())

    def handle_authorize(remote, token, user_info):
        if user_info:
            try:
                provider = remote if isinstance(remote, str) else remote.name
                user_id = _handle_user_auth(user_info, provider=provider)
                touch(user_id)
                session[f'{_d.x_prefix}user_id'] = user_id
                session[f'{_d.x_prefix}user_picture'] = user_info.picture
                session[f'{_d.x_prefix}user_name'] = _get_oauth_user_name(
                    user_info, provider)
                session[f'{_d.x_prefix}user_confirmed'] = True
                _call_handler('account.login', user_id=user_id)
                _log_user_event(f'account.login:{provider}')
                return redirect(_next_uri())
            except ResourceAlreadyExists:
                response = _call_handler('exception.provider_exists')
                return response if response else Response(
                    'oauth provider is already ' +
                    'registered for another account',
                    status=409)
            except AccessDenied:
                response = _call_handler('exception.registration_denied')
                return response if response else Response(
                    'account registration is disabled', status=403)
            # session.permanent = True
        else:
            response = _call_handler('exception.provider_failed')
            return response if response else Response('forbidden', status=403)

    def google_login():
        redirect_uri = url_for(f'{_d.dot_prefix}google.auth', _external=True)
        return oauth.google.authorize_redirect(redirect_uri)

    def google_auth():
        token = oauth.google.authorize_access_token()
        user_info = oauth.google.parse_id_token(token)
        return handle_authorize('google', token, user_info)

    for k in config:
        app.config[k.upper().replace('-', '_')] = config_value(config=config,
                                                               config_path=k)
    oauth = OAuth(app)
    app.add_url_rule(f'{base_prefix}/logout',
                     f'{_d.dot_prefix}.logout',
                     logout,
                     methods=['GET'])
    app.add_url_rule(f'{base_prefix}/confirm/<key>',
                     f'{_d.dot_prefix}confirm',
                     handle_confirm,
                     methods=['GET'])
    for p in providers:
        if p == 'google':
            oauth.register(
                'google',
                server_metadata_url=
                'https://accounts.google.com/.well-known/openid-configuration',
                client_kwargs={'scope': 'openid profile'},
            )
            app.add_url_rule(f'{base_prefix}/google/login',
                             f'{_d.dot_prefix}google.login',
                             google_login,
                             methods=['GET'])
            app.add_url_rule(f'{base_prefix}/google/auth',
                             f'{_d.dot_prefix}google.auth',
                             google_auth,
                             methods=['GET'])
        else:
            blueprint = loginpass.create_flask_blueprint(
                _provider_mod[p], oauth, handle_authorize)
            app.register_blueprint(blueprint, url_prefix=f'{base_prefix}/{p}')
    init_db()
    return