Example #1
0
def get_timed_signer_url(namespace):
    """
    This gives a basic signing/verifying object.

    The namespace makes sure signed tokens can't be used in
    a different area. Like using a forgot-password-token as
    a session cookie.

    Basic usage:

    .. code-block:: python

       _signer = None
       TOKEN_VALID_DAYS = 10
       def setup():
           global _signer
           _signer = get_timed_signer_url("session cookie")
       def create_token(obj):
           return _signer.dumps(obj)
       def parse_token(token):
           # This might raise an exception in case
           # of an invalid token, or an expired token.
           return _signer.loads(token, max_age=TOKEN_VALID_DAYS*24*3600)

    For more details see
    http://pythonhosted.org/itsdangerous/#itsdangerous.URLSafeTimedSerializer
    """
    assert __itsda_secret is not None
    return itsdangerous.URLSafeTimedSerializer(__itsda_secret, salt=namespace)
Example #2
0
    def claim(self):
        email = context.form.get('email')
        email_pattern = r'(^[a-zA-Z0-9_.+-]+@[a-zA-Z0-9-]+\.[a-zA-Z0-9-.]+$)'
        if not email:
            raise HTTPBadRequest()

        if not re.match(email_pattern, email):
            raise HTTPStatus('701 Invalid email format.')

        if DBSession.query(Member.email).filter(Member.email == email).count():
            raise HTTPStatus(
                '601 The requested email address is already registered.')

        # FIXME: Token should be put in payload
        serializer = \
            itsdangerous.URLSafeTimedSerializer(settings.activation.secret)
        token = serializer.dumps(email)
        DBSession.add(
            ActivationEmail(to=email,
                            subject='Activate your NueMD Coder account',
                            body={
                                'activation_token': token,
                                'activation_url': settings.activation.url
                            }))
        return dict(email=email)
Example #3
0
def unsubscribe(request, token):
    signer = itsdangerous.URLSafeTimedSerializer(
        secret_key=settings.SECRET_KEY)
    max_age = 60 * 60 * 24 * 7  # Seven days till the link expires

    try:
        subscription_id = signer.loads(token, max_age=max_age)
        get_object_or_404(Subscription, pk=subscription_id).delete()
        context = {
            'status': 'success',
        }
    except itsdangerous.SignatureExpired:
        context = {'status': 'failure', 'reason': 'expired'}
        logger.warning('Unsubscribe link expired')
    except itsdangerous.BadTimeSignature as e:
        logger.error('Wrong signature format [{}]: {}'.format(e, token))
        context = {'status': 'failure', 'reason': 'tampered'}
    except itsdangerous.BadSignature as e:
        context = {'status': 'failure', 'reason': 'tampered'}
        logger.error(
            'Tampering with unsubscribe link detected [{}]: {}'.format(
                e, token))

        encoded_payload = e.payload
        if encoded_payload is not None:
            try:
                decoded_payload = signer.load_payload(encoded_payload)
                logger.error('Payload was {}'.format(decoded_payload))
            except itsdangerous.BadData:
                pass

    return render(request,
                  'creator/subscription_unsubscribe.html',
                  context=context)
Example #4
0
def extract_session_from_cookie (cookie_header, secret_key):
    parsed_cookie = SimpleCookie (cookie_header)
    if not 'session' in parsed_cookie:
        return {}

    cookie_interface = flask.sessions.SecureCookieSessionInterface()

    # This code matches what Flask does for encoding
    serializer = itsdangerous.URLSafeTimedSerializer(
        secret_key,
        salt=cookie_interface.salt,
        serializer=cookie_interface.serializer,
        signer_kwargs=dict(
            key_derivation=cookie_interface.key_derivation,
            digest_method=cookie_interface.digest_method
       ))

    try:
        cookie_value = parsed_cookie['session'].value
        return serializer.loads(cookie_value)
    except itsdangerous.exc.BadSignature as e:
        # If the signature is wrong, we can still decode the cookie.
        # We try to do it properly with the actual key though, because exception
        # handling is slightly slow in Python and doing it successfully is therefore
        # faster.
        if e.payload is not None:
            try:
                return serializer.load_payload(e.payload)
            except itsdangerous.exc.BadData:
                pass
        return {}
Example #5
0
def generate_link(request, user):
    xom = request.registry['xom']
    serializer = itsdangerous.URLSafeTimedSerializer(xom.config.secret)
    result = {'hash_type': 'sha256', 'salt': newsalt(), 'username': user.name}
    result['token'] = generate_token(result, get_pwhash(user))
    value = serializer.dumps(result)
    return request.route_url('passwd_reset', token=value)
Example #6
0
def reset(password_reset):
    safe = itsdangerous.URLSafeTimedSerializer(settings.SECRET_KEY)

    with db.session.begin(subtransactions=True):
        user_ = db.session.query(User).filter(
            User.email == password_reset["email"]).one_or_none()

        NoSuchUser.require_condition(
            user_,
            "The user with {email} email does not exist",
            email=password_reset["email"])

        token = safe.dumps(user_.email)

        email.template.send(
            "password_reset",
            body={
                "from_": "*****@*****.**",
                "to": [user_.email],
                "subject": "Password reset required",
                "variables": {
                    "name":
                    user_.get_short_name(),
                    "password_reset_url":
                    password_reset["url_template"].format(token=token),
                },
                "tag": "password_reset",
            },
        )

    return connexion.NoContent, 200
Example #7
0
def confirm_self_deletion(token):
    """Confirm user deletion."""
    s = itsdangerous.URLSafeTimedSerializer(flask.current_app.config.get("SECRET_KEY"))

    try:

        # Get email from token, overwrite the one from login if applicable
        email = s.loads(token, salt="email-delete", max_age=604800)

        # Check that the email is registered on the current user:
        if email not in [email.email for email in flask_login.current_user.emails]:
            msg = "The email for user to be deleted is not registered on your account."
            flask.current_app.logger.warning(
                f"{msg} email: {email}: user: {flask_login.current_user}"
            )
            raise ddserr.UserDeletionError(message=msg)

        # Get row from deletion requests table
        deletion_request_row = models.DeletionRequest.query.filter(
            models.DeletionRequest.email == email
        ).first()

    except itsdangerous.exc.SignatureExpired as exc:

        email = db_tools.remove_user_self_deletion_request(flask_login.current_user)
        raise ddserr.UserDeletionError(
            message=f"Deletion request for {email} has expired. Please login to the DDS and request deletion anew."
        ) from exc
    except (itsdangerous.exc.BadSignature, itsdangerous.exc.BadTimeSignature) as exc:
        raise ddserr.UserDeletionError(
            message="Confirmation link is invalid. No action has been performed."
        ) from exc
    except sqlalchemy.exc.SQLAlchemyError as sqlerr:
        raise ddserr.DatabaseError(message=sqlerr) from sqlerr

    # Check if the user and the deletion request exists
    if deletion_request_row:
        try:
            user = user_schemas.UserSchema().load({"email": email})
            _ = db_tools.remove_user_self_deletion_request(user)
            DeleteUser.delete_user(user=user)

        except sqlalchemy.exc.SQLAlchemyError as sqlerr:
            raise ddserr.UserDeletionError(
                message=f"User deletion request for {user.username} / {user.primary_email.email} failed due to database error: {sqlerr}",
                alt_message=f"Deletion request for user {user.username} registered with {user.primary_email.email} failed for technical reasons. Please contact the unit for technical support!",
            )
        except sqlalchemy.exc.OperationalError as err:
            raise ddserr.DatabaseError(message=str(err), alt_message="Unexpected database error.")

        flask.session.clear()

        return flask.make_response(
            flask.render_template("user/userdeleted.html", username=user.username, initial=True)
        )
    else:
        return flask.make_response(
            flask.render_template("user/userdeleted.html", username=email, initial=False)
        )
Example #8
0
    def get_token(self, tag):
        """Return a token that can be used to validate or reset a user.

        The tag is a plain string that is used as a namespace. It must also be
        passed to from_token."""
        serialzer = itsdangerous.URLSafeTimedSerializer(
            current_app.config['SECRET_KEY'])
        return serialzer.dumps(self.id, salt=tag)
Example #9
0
    def from_token(token, expires=3600):
        auth_s = itsdangerous.URLSafeTimedSerializer(app.config['SECRET_KEY'])
        try:
            data = auth_s.loads(token, max_age=expires)
        except Exception as e:
            return None

        return User.query.get(data['id'])
Example #10
0
 def from_token(cls, token, tag, max_age=None):
     """Given a token, return the user for that token."""
     serialzer = itsdangerous.URLSafeTimedSerializer(
         current_app.config['SECRET_KEY'])
     try:
         id = serialzer.loads(token, max_age=max_age, salt=tag)
     except itsdangerous.BadData:
         return None
     return cls.query.get(int(id))
Example #11
0
def get_signing_serializer_modified(self, app):
    if not secret_key:
        return None
    signer_kwargs = dict(
        key_derivation=self.key_derivation,
        digest_method=self.digest_method
    )
    return itsdangerous.URLSafeTimedSerializer(secret_key, salt=self.salt,
                                               serializer=self.serializer,
                                               signer_kwargs=signer_kwargs)
Example #12
0
def factory(
    app,
    defauts,
    secret,
    audience='http://localhost:8080',
    prefix='/persona',
    ):

    serializer = itsdangerous.URLSafeTimedSerializer(secret)

    @bobo.subroute(prefix)
    def routes(request):
        return Routes(request, audience, prefix, serializer)

    persona_app = bobo.Application(bobo_resources = [routes])

    def run_app(env, start):
        request = webob.Request(env)

        token = request.cookies.get(TOKEN)
        old_email = email = None
        if token:
            try:
                email = serializer.loads(token)
            except itsdangerous.BadTimeSignature:
                pass # just don't log them in
            else:
                old_email = env.get('REMOTE_USER')
                env['REMOTE_USER'] = email

        try:
            if env['PATH_INFO'].startswith(prefix):
                return persona_app(env, start)
            else:
                needs_login = []
                def start_response(status, headers, exc_info=None):
                    if status.startswith("401 "):
                        needs_login.append(0)
                    else:
                        start(status, headers, exc_info)
                it = app(env, start_response)
                if needs_login:
                    return bobo.redirect(
                        prefix+'/login.html?came_from='+env['PATH_INFO']
                        )(env, start)
                else:
                    return it
        finally:
            if email:
                if old_email:
                    env['REMOTE_USER'] = old_email
                else:
                    del env['REMOTE_USER']

    return run_app
Example #13
0
 def confirm_token(token: str, expiration: int=3600) -> str:
     serializer = itsdangerous.URLSafeTimedSerializer(flask.current_app.config['SECRET_KEY'])
     try:
         email = serializer.loads(
             token,
             salt=flask.current_app.config['SECURITY_PASSWORD_SALT'],
             max_age=expiration
         )
         return email
     except:
         return ""
Example #14
0
 def _make_serializer(self) -> itsdangerous.URLSafeTimedSerializer:
     """Returns URL Safe Timed Serializer for signing payloads.
     
     :return: Serializer for dumping and loading into a URL safe string.
     :rtype: :class:`itsdangerous.URLSafeTimedSerializer`
     """
     return itsdangerous.URLSafeTimedSerializer(
         secret_key=self.secret, salt=self.salt, signer_kwargs={
             'key_derivation': 'hmac',
             'digest_method': 'SHA1'
         })
Example #15
0
def create_token(entity, contents=None, field=None, salt=b'itsdangerous'):
    assert isinstance(entity, SecureEntity)

    if callable(contents):
        contents = contents(entity)

    if not contents and field:
        contents = getattr(entity, field)

    return itsdangerous.URLSafeTimedSerializer(
        secret_key=entity.get_token_secret_key(), salt=salt).dumps(contents)
def unsubscribe(token):
    safe = itsdangerous.URLSafeTimedSerializer(settings.SECRET_KEY)

    token_data = safe.loads(token)

    with db.session.begin(subtransactions=True):
        # Create an unsubscribe
        unsubscribe_ = Unsubscribe(**token_data)
        db.session.add(unsubscribe_)

    return flask.render_template("unsubscribe.html"), 200
Example #17
0
def deserialize_url_time_sensitive_value(token, salt):
    serializer = itsd.URLSafeTimedSerializer(os.environ["SECRET_KEY"])
    try:
        decoded_payload = serializer.loads(token, salt=salt, max_age=86400)
        return decoded_payload
    except itsd.BadSignature as e:
        if e.payload is not None:
            try:
                decoded_payload = serializer.load_payload(e.payload)
                return None
            except itsd.BadData:
                return None
Example #18
0
def from_config(config: typing.Dict[str, typing.Any],
                secret_key: typing.Union[str, bytes],
                state_storage: dict = None) -> Authl:
    """ Generate an Authl handler set from provided configuration directives.

    Arguments:

    :param dict config: a configuration dictionary. See the individual handlers'
        from_config functions to see possible configuration values.
    :param std secret_key: a signing key used to keep authentication secrets.
    :param dict state_storage: a dict-like object that will store persistent
        state for methods that need it

    Handlers will be enabled based on truthy values of the following keys

        EMAIL_FROM / EMAIL_SENDMAIL -- enable the EmailAddress handler
        MASTODON_NAME -- enable the Mastodon handler
        INDIEAUTH_CLIENT_ID -- enable the IndieAuth handler
        INDIELOGIN_CLIENT_ID -- enable the IndieLogin handler
        TWITTER_CLIENT_KEY -- enable the Twitter handler
        TEST_ENABLED -- enable the test/loopback handler

    """

    serializer = itsdangerous.URLSafeTimedSerializer(secret_key)
    instance = Authl()

    if config.get('EMAIL_FROM') or config.get('EMAIL_SENDMAIL'):
        from .handlers import email_addr
        instance.add_handler(email_addr.from_config(config, serializer))

    if config.get('FEDIVERSE_NAME') or config.get('MASTODON_NAME'):
        from .handlers import fediverse
        instance.add_handler(fediverse.from_config(config, serializer))

    if config.get('INDIEAUTH_CLIENT_ID'):
        from .handlers import indieauth
        instance.add_handler(indieauth.from_config(config, serializer))

    if config.get('INDIELOGIN_CLIENT_ID'):
        from .handlers import indielogin
        instance.add_handler(indielogin.from_config(config, serializer))

    if config.get('TWITTER_CLIENT_KEY'):
        from .handlers import twitter
        instance.add_handler(twitter.from_config(config, state_storage))

    if config.get('TEST_ENABLED'):
        from .handlers import test_handler
        instance.add_handler(test_handler.TestHandler())

    return instance
Example #19
0
def edit_project(projectid):
    MAX_TOKEN_AGE = 3600
    isp = ISP.query.filter_by(id=projectid, is_disabled=False).first_or_404()
    sess_token = session.get('edit_tokens', {}).get(isp.id)

    if 'token' in request.args:
        s = itsdangerous.URLSafeTimedSerializer(current_app.secret_key,
                                                salt='edit')
        try:
            r = s.loads(request.args['token'],
                        max_age=MAX_TOKEN_AGE,
                        return_timestamp=True)
        except:
            abort(403)

        if r[0] != isp.id:
            abort(403)

        tokens = session.setdefault('edit_tokens', {})
        session.modified = True  # ITS A TARP
        tokens[r[0]] = r[1]
        # refresh page, without the token in the url
        return redirect(url_for('.edit_project', projectid=r[0]))
    elif (sess_token is None
          or (datetime.utcnow() - sess_token).total_seconds() > MAX_TOKEN_AGE):
        return redirect(url_for('.gen_edit_token', projectid=isp.id))

    if isp.is_local:
        form = forms.ProjectForm.edit_json(isp)
        if form.validate_on_submit():
            isp.name = form.name.data
            isp.shortname = form.shortname.data or None
            isp.json = form.to_json(isp.json)
            isp.tech_email = form.tech_email.data

            db.session.add(isp)
            db.session.commit()
            flash(_(u'Project modified'), 'info')
            return redirect(url_for('.project', projectid=isp.id))
        return render_template('edit_project_form.html', form=form)
    else:
        form = forms.ProjectJSONForm(obj=isp)
        if form.validate_on_submit():
            isp.tech_email = form.tech_email.data
            url = utils.make_ispjson_url(form.json_url.data)
            isp.json_url = url

            db.session.add(isp)
            db.session.commit()
            flash(_(u'Project modified'), 'info')
            return redirect(url_for('.project', projectid=isp.id))
        return render_template('edit_project_json_form.html', form=form)
Example #20
0
def get_token_info(request, token):
    xom = request.registry['xom']
    serializer = itsdangerous.URLSafeTimedSerializer(xom.config.secret)
    value = serializer.loads(token, max_age=24 * 60 * 60)
    user = xom.model.get_user(value['username'])
    if user is None:
        raise ValueError("The user '%s' doesn't exist anymore." %
                         value['username'])
    if value['token'] != generate_token(value, get_pwhash(user)):
        raise ValueError(
            "The password has already been changed since the link was requested."
        )
    return value
Example #21
0
 def authenticate(self, cookie_key, cookies):
     signer_kwargs = dict(key_derivation=self.key_derivation,
                          digest_method=self.digest_method)
     serializer = itsdangerous.URLSafeTimedSerializer(
         app.secret_key,
         salt=self.salt,
         serializer=self.serializer,
         signer_kwargs=signer_kwargs)
     morsel_val = cookies.get(cookie_key)
     if not morsel_val:
         return {}
     assert morsel_val.key == cookie_key
     session_data = serializer.loads(morsel_val.value)
     return session_data
Example #22
0
 def from_env() -> "App":
     config = environ.to_config(Config)
     app = flask.Flask(
         __name__,
         static_folder=os.path.join(os.getcwd(), config.static_path),
     )
     # 16MB
     app.config["MAX_CONTENT_LENGTH"] = 16 * 1024 * 1024
     app.secret_key = config.secret_key
     return App(
         config=config,
         db=playhouse.sqlite_ext.SqliteExtDatabase(None),
         serializer=itsdangerous.URLSafeTimedSerializer(config.secret_key),
         app=app,
     )
Example #23
0
    def _make_serializer(self) -> itsdangerous.URLSafeTimedSerializer:
        """Returns URL Safe Timed Serializer for signing payloads.

        :return: Serializer for dumping and loading into a URL safe string.
        :rtype: :class:`itsdangerous.URLSafeTimedSerializer`
        """
        # TODO: Throw exception if secret / salt not set.
        return itsdangerous.URLSafeTimedSerializer(
            secret_key=self.secret,
            salt=self.salt,
            signer_kwargs={
                "key_derivation": "hmac",
                "digest_method": "SHA1"
            },
        )
Example #24
0
def get_user_from_reset_token(token):

    serializer = itsdangerous.URLSafeTimedSerializer(app.secret_key)

    try:
        data = serializer.loads(token, max_age=86400)
    except itsdangerous.BadData:
        return ('invalid token', None)

    (email, password_hash) = data

    user = User.objects.filter(email=email).first()
    if user and md5(user.password.encode('utf-8')) == password_hash:
        return (None, user)

    return ('invalid token', None)
Example #25
0
def verify_token(token, entity_getter, salt=b'itsdangerous', max_age=None):
    data = peak_token_data(token)
    if not data:
        return None, None

    entity = entity_getter(data)
    if not entity:
        return None, data

    try:
        itsdangerous.URLSafeTimedSerializer(entity.get_token_secret_key(),
                                            salt).loads(token, max_age=max_age)
    except (itsdangerous.BadSignature, itsdangerous.SignatureExpired):
        return None, None

    return entity, data
Example #26
0
    def __call__(self, args):

        serializer = \
            itsdangerous.URLSafeTimedSerializer(settings.activation.secret)

        token = serializer.dumps(args.email)

        email = ActivationEmail(
                to=args.email,
                subject='Activate your Cucumber account',
                body={
                    'activation_token': token,
                    'activation_url': settings.activation.url
                }
        )
        email.to = args.email
        email.do_({})
Example #27
0
def instrument_log_mobile_file_upload(instrument_id: int, token: str):
    try:
        instrument = get_instrument(instrument_id)
    except InstrumentDoesNotExistError:
        return flask.abort(404)
    serializer = itsdangerous.URLSafeTimedSerializer(flask.current_app.config['SECRET_KEY'], salt='instrument-log-mobile-upload')
    try:
        user_id, instrument_id = serializer.loads(token, max_age=15 * 60)
    except itsdangerous.BadSignature:
        return flask.abort(400)
    try:
        user = get_user(user_id)
    except UserDoesNotExistError:
        return flask.abort(403)
    if not instrument.users_can_create_log_entries and user not in instrument.responsible_users:
        return flask.abort(403)
    if user.is_readonly:
        return flask.abort(403)
    return flask.render_template('mobile_upload.html', user_id=get_user(user_id), instrument=instrument)
Example #28
0
def post_instrument_log_mobile_file_upload(instrument_id: int, token: str):
    try:
        instrument = get_instrument(instrument_id)
    except InstrumentDoesNotExistError:
        return flask.abort(404)
    serializer = itsdangerous.URLSafeTimedSerializer(flask.current_app.config['SECRET_KEY'], salt='instrument-log-mobile-upload')
    try:
        user_id, instrument_id = serializer.loads(token, max_age=15 * 60)
    except itsdangerous.BadSignature:
        return flask.abort(400)
    try:
        user = get_user(user_id)
    except UserDoesNotExistError:
        return flask.abort(403)
    if not instrument.users_can_create_log_entries and user not in instrument.responsible_users:
        return flask.abort(403)
    if user.is_readonly:
        return flask.abort(403)
    files = flask.request.files.getlist('file_input')
    if not files:
        return flask.redirect(
            flask.url_for(
                '.instrument_log_mobile_file_upload',
                instrument_id=instrument_id,
                token=token
            )
        )
    if files:
        category_ids = []
        log_entry = create_instrument_log_entry(
            instrument_id=instrument_id,
            user_id=user_id,
            content='',
            category_ids=category_ids
        )
        for file_storage in files:
            create_instrument_log_file_attachment(
                instrument_log_entry_id=log_entry.id,
                file_name=file_storage.filename,
                content=file_storage.stream.read()
            )
    return flask.render_template('mobile_upload_success.html')
Example #29
0
def reset_confirm(password_reset_confirm):
    safe = itsdangerous.URLSafeTimedSerializer(settings.SECRET_KEY)

    with InvalidToken.handle_errors(
            "The specified token is invalid",
            exception_class=(itsdangerous.BadSignature,
                             itsdangerous.BadTimeSignature)):
        email = safe.loads(password_reset_confirm["token"])

    with db.session.begin(subtransactions=True):
        user_ = db.session.query(User).filter(
            User.email == email).one_or_none()

        NoSuchUser.require_condition(
            user_, "The user with {email} email does not exist", email=email)

        user_.password = guard.encrypt_password(
            password_reset_confirm["new_password"])

        db.session.add(user_)

    return connexion.NoContent, 200
Example #30
0
def gen_edit_token(projectid):
    isp = ISP.query.filter_by(id=projectid, is_disabled=False).first_or_404()
    form = forms.RequestEditToken()
    if form.validate_on_submit():  # validated
        if form.tech_email.data == isp.tech_email:
            s = itsdangerous.URLSafeTimedSerializer(current_app.secret_key,
                                                    salt='edit')
            token = s.dumps(isp.id)
            msg = Message("Edit request of your ISP",
                          sender=current_app.config['EMAIL_SENDER'])
            msg.body = """
Hello,
You are receiving this message because your are listed as technical contact for "%s" on the FFDN ISP database.

Someone asked to edit your ISP's data in our database. If it's not you, please ignore this message.

To proceed to the editing form, please click on the following link:
%s?token=%s

Note: the link is only valid for one hour from the moment we send you this email.

Thanks,
The FFDN ISP Database team
https://db.ffdn.org
            """.strip() % (isp.complete_name,
                           url_for('.edit_project',
                                   projectid=isp.id,
                                   _external=True), token)
            msg.add_recipient(isp.tech_email)
            mail.send(msg)

        # if the email provided is not the correct one, we still redirect
        flash(
            _(u'If you provided the correct email adress, '
              'you must will receive a message shortly (check your spam folder)'
              ), 'info')
        return redirect(url_for('.project', projectid=isp.id))

    return render_template('gen_edit_token.html', form=form)