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
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
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
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
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