Пример #1
0
    def generate_token(self, username=None, regenerate=True):
        """
		Generate and store a new registration token for this user

		Tokens are not re-generated if they exist already

		:param username:  Username to generate for: if left empty, it will be
		inferred from self.data
		:param regenerate:  Force regenerating even if token exists
		:return str:  The token
		"""
        if self.data.get("register_token", None) and not regenerate:
            return self.data["register_token"]

        if not username:
            username = self.data["name"]

        register_token = hashlib.sha256()
        register_token.update(os.urandom(128))
        register_token = register_token.hexdigest()
        db.update("users",
                  data={
                      "register_token": register_token,
                      "timestamp_token": int(time.time())
                  },
                  where={"name": username})

        return register_token
Пример #2
0
	def clear_token(self):
		"""
		Reset password rest token

		Clears the token and token timestamp. This allows requesting a new one
		even if the old one had not expired yet.

		:return:
		"""
		db.update("users", data={"register_token": "", "timestamp_token": 0}, where={"name": self.get_id()})
Пример #3
0
	def set_value(self, key, value):
		"""
		Set persistently stored user property

		:param key:  Name of item to store
		:param value:  Value
		:return:
		"""
		self.userdata[key] = value

		db.update("users", where={"name": self.get_id()}, data={"userdata": json.dumps(self.userdata)})
Пример #4
0
	def set_password(self, password):
		"""
		Set user password

		:param password:  Password to set
		"""
		if self.is_anonymous:
			raise Exception("Cannot set password for anonymous user")

		salt = bcrypt.gensalt()
		password_hash = bcrypt.hashpw(password.encode("ascii"), salt)

		db.update("users", where={"name": self.data["name"]}, data={"password": password_hash.decode("utf-8")})
Пример #5
0
	def __init__(self, data, authenticated=False):
		"""
		Instantiate user object

		Also sets the properties required by Flask-Login.

		:param data:  User data
		:param authenticated:  Whether the user should be marked as authenticated
		"""
		self.data = data

		if self.data["name"] != "anonymous":
			self.is_anonymous = False
			self.is_active = True

		self.name = self.data["name"]
		self.is_authenticated = authenticated

		self.userdata = json.loads(self.data.get("userdata", "{}"))

		if not self.is_anonymous and self.is_authenticated:
			db.update("users", where={"name": self.data["name"]}, data={"timestamp_seen": int(time.time())})
Пример #6
0
def add_user():
    """
	Create a new user

	Sends the user an e-mail with a link through which they can set their
	password.

	:return: Either an html page with a message, or a JSON response, depending
	on whether ?format == html
	"""
    if not current_user.is_authenticated or not current_user.is_admin():
        return error(403, message="This page is off-limits to you.")

    response = {"success": False}

    email = request.form.get("email", request.args.get("email", "")).strip()
    fmt = request.form.get("format", request.args.get("format", "")).strip()
    force = request.form.get("force", request.args.get("force", None))

    if not email or not re.match(r"[^@]+\@.*?\.[a-zA-Z]+", email):
        response = {
            **response,
            **{
                "message": "Please provide a valid e-mail address."
            }
        }
    else:
        username = email
        try:
            db.insert("users",
                      data={
                          "name": username,
                          "timestamp_token": int(time.time())
                      })

            user = User.get_by_name(username)
            if user is None:
                response = {
                    **response,
                    **{
                        "message":
                        "User was created but could not be instantiated properly."
                    }
                }
            else:
                try:
                    user.email_token(new=True)
                    response["success"] = True
                    response = {
                        **response,
                        **{
                            "message":
                            "An e-mail containing a link through which the registration can be completed has been sent to %s." % username
                        }
                    }
                except RuntimeError as e:
                    response = {
                        **response,
                        **{
                            "message":
                            "User was created but the registration e-mail could not be sent to them (%s)." % e
                        }
                    }
        except psycopg2.IntegrityError:
            db.rollback()
            if not force:
                response = {
                    **response,
                    **{
                        "message":
                        'Error: User %s already exists. If you want to re-create the user and re-send the registration e-mail, use [this link](/admin/add-user?email=%s&force=1&format=%s).' % (username, username, fmt)
                    }
                }
            else:
                # if a user does not use their token in time, maybe you want to
                # be a benevolent admin and give them another change, without
                # having them go through the whole signup again
                user = User.get_by_name(username)
                db.update("users",
                          data={"timestamp_token": int(time.time())},
                          where={"name": username})

                try:
                    user.email_token(new=True)
                    response["success"] = True
                    response = {
                        **response,
                        **{
                            "message":
                            "A new registration e-mail has been sent to %s." % username
                        }
                    }
                except RuntimeError as e:
                    response = {
                        **response,
                        **{
                            "message":
                            "Token was reset registration e-mail could not be sent to them (%s)." % e
                        }
                    }

    if fmt == "html":
        return render_template(
            "error.html",
            message=response["message"],
            title=("New account created" if response["success"] else "Error"))
    else:
        return jsonify(response)
Пример #7
0
    def email_token(self, new=False):
        """
		Send user an e-mail with a password reset link

		Generates a token that the user can use to reset their password. The
		token is valid for 72 hours.

		Note that this requires a mail server to be configured, or a
		`RuntimeError` will be raised. If a server is configured but the mail
		still fails to send, it will also raise a `RuntimeError`. Note that
		in these cases a token is still created and valid (the user just gets
		no notification, but an admin could forward the correct link).

		If the user is a 'special' user, a `ValueError` is raised.

		:param bool new:  Is this the first time setting a password for this
						  account?
		"""
        if not config.MAILHOST:
            raise RuntimeError(
                "No e-mail server configured. 4CAT cannot send any e-mails.")

        if self.is_special():
            raise ValueError(
                "Cannot send password reset e-mails for a special user.")

        username = self.get_id()

        # generate a password reset token
        register_token = hashlib.sha256()
        register_token.update(os.urandom(128))
        register_token = register_token.hexdigest()
        db.update("users",
                  data={
                      "register_token": register_token,
                      "timestamp_token": int(time.time())
                  },
                  where={"name": username})

        # prepare welcome e-mail
        sender = "*****@*****.**"
        message = MIMEMultipart("alternative")
        message["Subject"] = "Account created"
        message["From"] = sender
        message["To"] = username

        # the actual e-mail...
        url_base = config.FlaskConfig.SERVER_NAME
        url = "https://%s/reset-password/?token=%s" % (url_base,
                                                       register_token)

        # we use slightly different e-mails depending on whether this is the first time setting a password
        if new:
            mail = """
			<p>Hello %s,</p>
			<p>A 4CAT account has been created for you. You can now log in to 4CAT at <a href="http://%s">%s</a>.</p>
			<p>Note that before you log in, you will need to set a password. You can do so via the following link:</p>
			<p><a href="%s">%s</a></p> 
			<p>Please complete your registration within 72 hours as the link above will become invalid after this time.</p>
			""" % (username, url_base, url_base, url, url)
        else:
            mail = """
			<p>Hello %s,</p>
			<p>Someone has requested a password reset for your 4CAT account. If that someone is you, great! If not, feel free to ignore this e-mail.</p>
			<p>You can change your password via the following link:</p>
			<p><a href="%s">%s</a></p> 
			<p>Please do this within 72 hours as the link above will become invalid after this time.</p>
			""" % (username, url, url)

        # provide a plain-text alternative as well
        html_parser = html2text.HTML2Text()
        message.attach(MIMEText(html_parser.handle(mail), "plain"))
        message.attach(MIMEText(mail, "html"))

        # try to send it
        try:
            with smtplib.SMTP(config.MAILHOST) as smtp:
                smtp.sendmail("*****@*****.**", [username], message.as_string())
        except (smtplib.SMTPException, ConnectionRefusedError) as e:
            raise RuntimeError("Could not send password reset e-mail: %s" % e)