Exemple #1
0
def main():
    """Testing 'argon2-cffi' package"""
    parser = argparse.ArgumentParser()
    parser.add_argument(
        "-v",
        "--verbosity",
        dest="verbosity",
        action="count",
        default=0,
        help="set verbosity level",
    )
    args = parser.parse_args()

    if args.verbosity == 1:
        logging.basicConfig(level=logging.INFO)
    elif args.verbosity > 1:
        logging.basicConfig(level=logging.DEBUG)
    else:
        logging.basicConfig(level=logging.ERROR)

    logging.debug(f"{argon2.__name__} {argon2.__version__}")

    ph = PasswordHasher()
    dummy_hash = ph.hash("s3kr3tp4ssw0rd")
    ph.verify(dummy_hash, "s3kr3tp4ssw0rd")
    ph.check_needs_rehash(dummy_hash)
    def test_check_needs_rehash_no(self):
        """
        Return False if the hash has the correct parameters.
        """
        ph = PasswordHasher(1, 8, 1, 16, 16)

        assert not ph.check_needs_rehash(ph.hash("foo"))
Exemple #3
0
def login():
    """Retrieve a token"""
    data = request.get_json()
    user = User.query.filter_by(username=data["username"]).first()

    if not user:
        return jsonify({"message": "Invalid credentials"}), 401

    try:
        hasher = PasswordHasher()
        hasher.verify(user.password, data["password"])
    except (VerifyMismatchError, VerificationError, InvalidHash,
            AttributeError):
        # This must also return 401 or else the user may learn
        # private information
        return jsonify({"message": "Invalid credentials"}), 401

    # Rehash password if the parameters of the PasswordHasher change.
    # https://argon2-cffi.readthedocs.io/en/stable/api.html
    if hasher.check_needs_rehash(user.password):
        user.password = hasher.hash(data["password"])
        db.session.add(user)
        db.session.commit()

    access_token = create_access_token(identity=user.username, fresh=True)
    refresh_token = create_refresh_token(user.username)

    return (jsonify({
        "access_token": access_token,
        "refresh_token": refresh_token
    }), 200)
    def login(self, username, password):
        success = False
        msg = ''
        userID = 'N/A'
        key = 'N/A'

        self.cursor.execute(
            """select password, id from users
			where username = %s""", (username, ))

        if self.cursor.rowcount == 0:
            success = False
            msg += '\n\tUsername does not exists'

        elif self.cursor.rowcount != 1:
            success = False
            msg += '\n\tUnknown Error'

        else:
            row = self.cursor.fetchone()
            hashed = row[0]
            userID = row[1]

            verifier = PasswordHasher()
            try:
                verifier.verify(hashed, password)
                success = True
            except:
                success = False

            if success:
                msg += 'Authenticated'

                if verifier.check_needs_rehash(hashed):
                    hashed = verifier.hash(password)
                    count = self.cursor.execute(
                        """update users
						set password = %s
						where username = %s""", (hashed, username))

                    if count != 1:
                        msg += 'Failed to rehash password, contact support'
                        logging.error('Failed to rehash password')
                        self.db.rollback()
                    else:
                        self.db.commit()

                self.cursor.execute(
                    """select activationKey from productKey
					where userID = %s""", (userID, ))

                row = self.cursor.fetchone()
                key = row[0]

            else:
                success = False
                msg += 'Incorrect Password'

        return success, msg, userID, key
    def test_check_needs_rehash_yes(self):
        """
        Return True if any of the parameters changes.
        """
        ph = PasswordHasher(1, 8, 1, 16, 16)
        ph_old = PasswordHasher(1, 8, 1, 8, 8)

        assert ph.check_needs_rehash(ph_old.hash("foo"))
def login():
    """Handle login requests

    On successfull login a session with a length of an hour should be generated.
    On failure an error should be thrown.
    """
    hasher = PasswordHasher()

    payload = request.json
    username = payload['user']
    password = payload['pass']

    DBSessionMaker = sessionmaker(bind=engine)
    db_session = DBSessionMaker()

    try:
        user = db_session.query(Users).get(username)
        hasher.verify(user.password, password)

        # If the hash parameters are out of date or the user update the hash
        # One example of an out of date parameter is a insufficient time cost
        if hasher.check_needs_rehash(user.password):
            user.password = hasher.hash(password)

        # calculate an expiration time one our from now
        expire = datetime.utcnow() + timedelta(hours=1)

        session_id = uuid.uuid4().hex

        user_session = Sessions(
            # This must be a uuid4 or large cryptographically secure random number
            # A other formats of UUID can have several bytes perdicted presenting
            # a potential brute forcing risk
            # This implimentation choice assumes CPython is being used.
            id=session_id,
            username=username,
            session_expire=expire
        )
        db_session.add(user_session)
        db_session.commit()

        auth_resp = Response(status=200)
        auth_resp.set_cookie('session', session_id)
        db_session.commit()
        return auth_resp

    # For security reasons only two responses may be provided
    # Thus errors do not need to be handled individually
    except Exception:
        db_session.rollback()

    # If we get to the end of this funtion something went wrong.
    # Thus make sure the user does not think they are logged in
    err_resp = Response(status=401)
    err_resp.delete_cookie('session')
    return err_resp
def compare_passwords(password, stored_password):
    """compares a plaintext password with the stored, encrypted password"""
    ph = PasswordHasher()
    try:
        ph.verify(stored_password, password)
        if ph.check_needs_rehash(stored_password):
            return (True, True)
        return (True, False)
    except VerifyMismatchError:
        return (False, False)
    def test_type_is_configurable(self):
        """
        Argon2id is default but can be changed.
        """
        ph = PasswordHasher(time_cost=1, memory_cost=64)
        default_hash = ph.hash("foo")

        assert Type.ID is ph.type is ph._parameters.type
        assert Type.ID is extract_parameters(default_hash).type

        ph = PasswordHasher(time_cost=1, memory_cost=64, type=Type.I)

        assert Type.I is ph.type is ph._parameters.type
        assert Type.I is extract_parameters(ph.hash("foo")).type
        assert ph.check_needs_rehash(default_hash)
Exemple #9
0
async def check_password(conn, name, password):
    """Check password and rotate cleartext password if needed"""
    from pakreq.utils import password_hash, password_verify
    users = await get_users(conn)
    status = False
    for user in users:
        if user['username'] == name or str(user['id']) == name:
            hash = user['password_hash']
            if password_verify(user['id'], password, hash):
                status = True
                hasher = PasswordHasher()
                if hasher.check_needs_rehash(hash):
                    hash = password_hash(user['id'], password)
                    await update_user(conn, user['id'], password_hash=hash)

    return status
Exemple #10
0
 async def check_password(self, hasher: PasswordHasher, pwd: str, pepper: bytes) -> bool:
     """
     compares a password to the password of the user
     :param hasher: PasswordHasher of MCWeb instance
     :param pwd: the entered password
     :param pepper: pepper  of the MCWeb instance
     :return: whether the password is correct
     """
     spp = self.salt + pwd.encode() + pepper
     try:
         if hasher.verify(self.password, spp):
             if hasher.check_needs_rehash(self.password):
                 hsh = hasher.hash(spp)
                 await self.db["user"].update_one({"_id": self.id}, {"$set": {"password": hsh}})
             return True
     except VerifyMismatchError:
         return False
    def verify_password(self):
        passwords = None
        write_back = False
        hasher = PasswordHasher()

        with open(os.path.join(DATA_DIR, PASSWORD_FILE),
                  'r',
                  encoding='utf8',
                  errors='ignore') as pw_file:
            try:
                passwords = json.load(pw_file)
            except json.decoder.JSONDecodeError:
                self.logger.error(
                    f"Could not parse password file, make sure it exsits and is not corrupted"
                )
                return False

        if self.user in passwords.keys():
            try:
                hasher.verify(passwords[self.user], self.password)
                write_back = hasher.check_needs_rehash(passwords[self.user])
            except VerifyMismatchError:
                self.logger.warning(
                    f"Password verification failed for ID {self.user} from {self.client_ip} on port {self.client_port}"
                )
                return False
        else:
            self.logger.warning(
                f"Password verification failed for ID {self.user} from {self.client_ip} on port {self.client_port}"
            )
            return False

        if write_back:
            self.logger.info(f"Rehashing ID {self.user}'s password")
            passwords[self.user] = hasher.hash(self.password)
            with open(os.path.join(DATA_DIR, PASSWORD_FILE),
                      'w',
                      encoding='utf8',
                      errors='ignore') as pw_file:
                json.dump(passwords, pw_file, ensure_ascii=False, indent=4)

        self.logger.info(
            f"Password verification successful for ID {self.user} from {self.client_ip} on port {self.client_port}"
        )
        return True
Exemple #12
0
    def verifyadmin(self, username, userpass):
        """
        Verifies if the admin with the given username and password exists.

        Args:
            username (str): Username of admin to verify.
            userpass (str): Password of admin to verify.

        Retruns:
            True if the admin exists and valid password, otherwise false.
            Admin ID if the user exists and valid password, None otherwise.

        """
        # Initialse session
        with self.sessionmanager() as session:
            # Initialise password hasher
            ph = PasswordHasher()
            # Query if admin exists
            admin = session.query(Admin).filter(
                   Admin.username == username).first()
            # Check if query returns an admin
            if(admin is not None):
                # Verify whether the password is valid or not
                try:
                    ph.verify(admin.passhash, userpass)
                except VerifyMismatchError:
                    # Password does not match, return false
                    return False, None
                # Check if password needs to be rehashed
                if(ph.check_needs_rehash(admin.passhash)):
                    # Generate new hash
                    rehash = ph.hash(userpass)
                    # Update admin record to include new hash
                    user.passhash = rehash
                # Since admin exists and password is valid, return true
                return True, admin.adminID
            else:
                # User doesn't exist, return false
                return False, None
Exemple #13
0
def create_api_key():
    """Creates a new API key and returns it to the user."""
    # Create a new secret and hash it
    pass_hasher = PasswordHasher()
    secret = secrets.token_urlsafe(64)
    hash = pass_hasher.hash(secret)

    # verify the secret
    if not pass_hasher.verify(hash, secret):
        return False

    if pass_hasher.check_needs_rehash(hash):
        return False

    # insert the secret into the DB
    query = "INSERT INTO credential (password_hash) VALUES (%s) RETURNING credential_id"
    data = (hash, )

    credential_id = insert(query, data, return_inserted_row_id=True)

    if not credential_id:
        return False

    return {'credential_id': credential_id, 'secret': secret}
Exemple #14
0
def _verify_password(credentials: CredentialsDTO) -> None:
    user = repo.find_by_username(credentials.username)
    ph = PasswordHasher()
    ph.verify(user.password_hash, credentials.password)
    if ph.check_needs_rehash(user.password_hash):
        repo.update_pw_hash(user, ph.hash(credentials.password))
from argon2 import PasswordHasher
ph = PasswordHasher()
hash = ph.hash("s3kr3tp4ssw0rd")
print(hash)  # doctest: +SKIP

#'$argon2id$v=19$m=102400,t=2,p=8$tSm+JOWigOgPZx/g44K5fQ$WDyus6py50bVFIPkjA28lQ'
print(ph.verify(hash, "s3kr3tp4ssw0rd"))
print(ph.check_needs_rehash(hash))
print(ph.verify(hash, "t0t411ywr0ng"))
Exemple #16
0
def authenticate():
    """
    Authenticates the user with the supplied credentials, returning
    an authentication token for subsequent use with the api.
    ---
    parameters:
      - in: body
        name: credentials
        schema:
            type: object
            properties:
              email:
                  type: string
                  required: true
              password:
                  type: string
                  required: true
    responses:
      200:
        schema:
          $ref: "#/definitions/Post"
      401:
        description: Unauthorized
    """
    body = request.json

    email = body.get("email")
    password = body.get("password")

    if email is None or password is None:
        return error(400,
                     message="`email` and `password` fields"
                     "are required.")

    user = User.query.filter(User.email == email).one_or_none()

    if user is None:
        return error(401, type="InvalidCredentials")

    hasher = PasswordHasher()

    try:
        hasher.verify(user.password_hash, password)
    except VerifyMismatchError:
        return error(401, type="InvalidCredentials")

    if hasher.check_needs_rehash(user.password_hash):
        hash = hasher.hash(password)
        user.password_hash = hash
        db.session.commit()

    if not user.confirmed:
        return error(401, type="Unconfirmed")

    now = time.time()
    expiration_time = now + 2 * 60 * 60

    claims = {
        "iat": int(now),
        "exp": int(expiration_time),
        "iss": config.JWT_ISSUER,
        "aud": config.JWT_AUDIENCE,
        "sub": email,
        "rol": "admin" if user.role == UserRole.ADMIN else "normal",
    }

    token = encode(claims, config.JWT_SECRET_KEY, algorithm="HS256")

    return token_response(user.id,
                          token.decode("utf-8"),
                          user.role,
                          int(expiration_time))
Exemple #17
0
class AuthController:
    instance = None

    @staticmethod
    def get_instance():
        if AuthController.instance is None:
            AuthController.instance = AuthController()
        return AuthController.instance

    def __init__(self):
        self.SECRET = "ABC123"
        self.ph = PasswordHasher()

    def authenticate(self, pw_hash: str, password: str) -> Dict:
        pw_verified: bool = self.ph.verify(pw_hash, password)
        new_pw_hash = None
        if pw_verified and self.ph.check_needs_rehash(pw_hash):
            new_pw_hash = self.ph.hash(password)
        if not pw_verified:
            raise Unauthorized("User or password is incorrect")
        return {'new_pw_hash': new_pw_hash, 'is_authenticated': True}

    def create_pw_hash(self, password: str) -> str:
        return self.ph.hash(password)

    def authorize(self, authorized_roles: List[str]):
        def decorated_function(function):
            @wraps(function)
            def wrapper(*args, **kwargs):
                self._has_authorized_roles(authorized_roles)
                return function(*args, **kwargs)

            return wrapper

        return decorated_function

    def _has_authorized_roles(self, authorized_roles: List[str]):
        # Check that auth cookie exist in request
        try:
            token = request.cookies.get("Authorization")
        except KeyError:
            raise Unauthorized("You need to be logged in")
        # Try to decode cookie to ensure user is authenticated
        user_data = self.decode_jwt_token(token)
        if (len(authorized_roles) == 0):
            return
        try:
            for role in authorized_roles:
                if role in user_data["roles"]:
                    return
            raise Unauthorized("You are not authorized to perform this action")
        except KeyError:
            raise Unauthorized(
                "You don't have permission to perform this action")

    def create_jwt_token(self,
                         user_id: int,
                         username: str,
                         roles: List = None) -> bytes:
        try:
            payload = {
                'exp': datetime.utcnow() + timedelta(hours=3),
                'iat': datetime.utcnow(),
                'sub': dict(user_id=user_id, username=username, roles=roles),
            }
            return jwt.encode(payload, self.SECRET, algorithm='HS256')
        except Exception as e:
            print(e)

    def decode_jwt_token(self, jwt_token: bytes) -> Dict:
        if jwt_token is None:
            raise Unauthorized("You need to be signed in")
        try:
            payload = jwt.decode(jwt_token, self.SECRET, algorithms='HS256')
            return payload['sub']
        except jwt.ExpiredSignatureError:
            raise Unauthorized("Token has expired, please sign in again")
        except jwt.InvalidTokenError:
            raise Unauthorized("Token is invalid, please sign in again")
Exemple #18
0
class AuthAndToken:
    def __init__(self,
                 sec_key,
                 token_exp_in_minutes=60,
                 time_cost=2,
                 memory_cost=102400,
                 parallelism=8,
                 hash_len=16,
                 salt_len=16):
        self.ph = PasswordHasher(time_cost, memory_cost, parallelism, hash_len,
                                 salt_len)
        self.secret_key = sec_key
        self.token_exp = token_exp_in_minutes

    def __verify_user(self, token):
        """
            receives a jwt token and verifies it's parameters
        """
        key = str(self.secret_key)
        try:
            jwt.decode(token, key, algorithms=['HS256'])
        except (jwt.ExpiredSignatureError, jwt.exceptions.DecodeError):
            return False
        return True

    def create_password(self, password):
        password = self.ph.hash(password)
        return password

    def verify_password(self, given_password, hash_password):
        """
            Validates if a password is valid or not and checks if rehashing is required or not(if valid)
            :param:
             * username
             * supplied password
            
            :returns:
             * validation status (True/False)
             * updated password(if needed) else ''
        """
        try:
            if self.ph.verify(hash_password.encode('utf-8'), given_password):
                if self.ph.check_needs_rehash(hash_password):
                    # rehash password if needed
                    hash_password = self.ph.hash(given_password)
                    return True, hash_password
            return True, ''
        except (VerificationError, VerifyMismatchError):
            return False, ''

    def validate_request(self, required=None, token_name='token'):
        """
            Decorator for validating Token and all required fields for a request
            :param:
             * required fields (if any): Dictionary of format:
                {<request_method> : [list of required fields]} => eg: {"POST": ["id", "password"]}
             * token name used in the header [default is 'token']
        """
        if required is None:
            required = {}

        def wrapper_layer2(func):
            @functools.wraps(func)
            @required_fields(required=required)
            def wrapper_layer1(*args, **kws):
                # checking JWT token
                token = request.headers.get(token_name)
                if not token:
                    return {
                        'error': True,
                        "message": "Token Required!",
                        "token404": True
                    }, 401
                if not self.__verify_user(token):
                    return {
                        'error': True,
                        "message": "Invalid Token. Try logging in!",
                        "unauth": True
                    }, 403

                return func(*args, **kws)

            return wrapper_layer1

        return wrapper_layer2

    def get_token(self, user):
        """
            Generates a new JWT
        """
        key = str(self.secret_key)
        claims = {
            'user':
            user,
            'time':
            str(datetime.datetime.utcnow()),
            'exp':
            datetime.datetime.utcnow() +
            datetime.timedelta(minutes=self.token_exp),
        }
        return jwt.encode(claims, key, algorithm='HS256')

    def get_user(self, token):
        claims = jwt.decode(token, verify=False)
        return str(claims['user'])
class Authenticator:
    """Class for registration and login of users."""

    def __init__(self, mongo_db: Database):
        self._mongo_db = mongo_db
        self._pwd_hasher = PasswordHasher()

    def _is_email(self, login: str) -> bool:
        """Check if login is email or username.

        :param login: login from input.
        :type login: str
        :return: true if email, false if username.
        :rtype: bool
        """
        return "@" in login

    def _rehash_password(self, user, password: str):
        """hash password again if Argon2 parameters change.

        :param user: User Document from mongo.
        :type user: Document
        :param password: Password to rehash.
        :type password: str
        """
        try:
            new_hashed_pwd = self._pwd_hasher.hash(password)
            self._mongo_db.users.update_one(
                {"username": user.username}, {"$set": {"password": new_hashed_pwd}}
            )
        except HashingError as err:
            logger.exception(err)

    def _create_token(self, size: int):
        return b64encode(urandom(size)).decode("utf-8")

    def register_user(self, username: str, password: str, email: str) -> dict:
        """Register a new user.

        :param username: Username of user.
        :type username: str
        :param password: Password of user.
        :type password: str
        :param email: Email of user.
        :type email: str
        :return: bool about failed/success login, user token and userID if success.
        :rtype: (bool, str)
        """
        if not username:
            raise ValueError("cannot register user, didnt get username")
        if not password:
            raise ValueError("cannot register user, didnt get password")
        if not email:
            raise ValueError("cannot register user, didnt get email")

        exists = self._mongo_db.users.count_documents(
            {"$or": [{"username": username}, {"email": email}]}, limit=1
        )
        if exists > 0:
            return (False, "Username or email already registered.")
        try:
            token = self._create_token(128)

            _id = self._mongo_db.users.insert_one(
                {
                    "username": username,
                    "password": self._pwd_hasher.hash(password),
                    "email": email,
                    "created": datetime.now(),
                    "last_login": datetime.now(),
                    "tokens": [token],
                    "collections": {},
                }
            ).inserted_id
            return {"success": True, "token": token, "id": _id, "username": username}
        except HashingError as err:
            logger.exception(err)

    def login_user(self, login: str, password: str) -> dict:
        """Validate password and return dictionary according to result.

        :param login: Username or email of user.
        :type login: str
        :param password: Password of user.
        :type password: str
        :return: {success: bool, user: userID, error: str}.
        :rtype: dict
        """
        user = (
            self._mongo_db.users.find_one({"email": login})
            if self._is_email(login)
            else self._mongo_db.users.find_one({"username": login})
        )
        if user is None:
            return {"success": False, "user": 0, "error": "Failed to verify user."}
        try:
            if self._pwd_hasher.verify(user["password"], password):
                # If user was verified, rehash password if it's needed.
                if self._pwd_hasher.check_needs_rehash(user["password"]):
                    self._rehash_password(user, password)

                token = self._create_token(128)
                self._mongo_db.users.update_one(
                    {"_id": ObjectId(user["_id"])}, {"$push": {"tokens": token}}
                )
                return {
                    "success": True,
                    "id": str(user["_id"]),
                    "token": token,
                    "username": user["username"],
                }
            return {"success": False, "user": 0, "error": "Failed to verify user."}

        except (VerificationError, VerifyMismatchError) as err:
            logger.info(err)
        except InvalidHash as err:
            logger.exception(err)

    def logout_user(self, token: str, user_id: str) -> dict:
        """Logout user with deleting his login token from the database.

        :param token: Username or email of user.
        :type token: str
        :return: {success: bool, user: userID, error: str}.
        :rtype: dict
        """
        user = self._mongo_db.users.update_one(
            {"_id": ObjectId(user_id)}, {"$pull": {"tokens": token}}
        )
        if user.modified_count < 1:
            return {"success": False, "message": "User or token not found."}
        return {"success": True, "message": "token removed from database"}
debugmode = os.getenv("DEBUG") != None and os.getenv("DEBUG").lower() == "true"
app = Flask(__name__)


def firsttimepass():
    password = input("What would you like to choose as a password? ")
    hash = ph.hash(password)
    with open("password.txt", "w") as f:
        f.write(hash)


if os.path.isfile("password.txt"):
    with open("password.txt", "r") as f:
        hash = f.read()
    try:
        if ph.check_needs_rehash(hash):
            # invalid hash
            os.remove("password.txt")
            firsttimepass()
    except InvalidHash:
        os.remove("password.txt")
        firsttimepass()
else:
    firsttimepass()


def verify(hash, passw):
    try:
        ph.verify(hash, passw)
    except Exception as e:
        if isinstance(e,