mirror of
https://github.com/clearml/clearml-server
synced 2025-06-26 23:15:47 +00:00
Add bcrypt support to fixed user password
This commit is contained in:
parent
29de110abb
commit
eab33de97e
@ -80,8 +80,10 @@
|
|||||||
}
|
}
|
||||||
|
|
||||||
# # A list of fixed users
|
# # A list of fixed users
|
||||||
|
# # Note: password may be bcrypt-hashed (generate using `python -c 'import bcrypt; print(bcrypt.hashpw("password", bcrypt.gensalt()))'`)
|
||||||
# fixed_users {
|
# fixed_users {
|
||||||
# enabled: true
|
# enabled: true
|
||||||
|
# pass_hashed: false
|
||||||
# users: [
|
# users: [
|
||||||
# {
|
# {
|
||||||
# username: "john"
|
# username: "john"
|
||||||
|
|||||||
@ -1,4 +1,5 @@
|
|||||||
attrs>=19.1.0
|
attrs>=19.1.0
|
||||||
|
bcrypt>=3.1.4
|
||||||
boltons>=19.1.0
|
boltons>=19.1.0
|
||||||
boto3==1.14.13
|
boto3==1.14.13
|
||||||
dpath>=1.4.2,<2.0
|
dpath>=1.4.2,<2.0
|
||||||
|
|||||||
@ -1,6 +1,7 @@
|
|||||||
import base64
|
import base64
|
||||||
from datetime import datetime
|
from datetime import datetime
|
||||||
|
|
||||||
|
import bcrypt
|
||||||
import jwt
|
import jwt
|
||||||
from mongoengine import Q
|
from mongoengine import Q
|
||||||
|
|
||||||
@ -31,8 +32,8 @@ def get_auth_func(auth_type):
|
|||||||
|
|
||||||
|
|
||||||
def authorize_token(jwt_token, *_, **__):
|
def authorize_token(jwt_token, *_, **__):
|
||||||
""" Validate token against service/endpoint and requests data (dicts).
|
"""Validate token against service/endpoint and requests data (dicts).
|
||||||
Returns a parsed token object (auth payload)
|
Returns a parsed token object (auth payload)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
return Token.from_encoded_token(jwt_token)
|
return Token.from_encoded_token(jwt_token)
|
||||||
@ -51,13 +52,15 @@ def authorize_token(jwt_token, *_, **__):
|
|||||||
|
|
||||||
|
|
||||||
def authorize_credentials(auth_data, service, action, call_data_items):
|
def authorize_credentials(auth_data, service, action, call_data_items):
|
||||||
""" Validate credentials against service/action and request data (dicts).
|
"""Validate credentials against service/action and request data (dicts).
|
||||||
Returns a new basic object (auth payload)
|
Returns a new basic object (auth payload)
|
||||||
"""
|
"""
|
||||||
try:
|
try:
|
||||||
access_key, _, secret_key = base64.b64decode(auth_data.encode()).decode('latin-1').partition(':')
|
access_key, _, secret_key = (
|
||||||
|
base64.b64decode(auth_data.encode()).decode("latin-1").partition(":")
|
||||||
|
)
|
||||||
except Exception as e:
|
except Exception as e:
|
||||||
log.exception('malformed credentials')
|
log.exception("malformed credentials")
|
||||||
raise errors.unauthorized.BadCredentials(str(e))
|
raise errors.unauthorized.BadCredentials(str(e))
|
||||||
|
|
||||||
query = Q(credentials__match=Credentials(key=access_key, secret=secret_key))
|
query = Q(credentials__match=Credentials(key=access_key, secret=secret_key))
|
||||||
@ -67,18 +70,32 @@ def authorize_credentials(auth_data, service, action, call_data_items):
|
|||||||
if FixedUser.enabled():
|
if FixedUser.enabled():
|
||||||
fixed_user = FixedUser.get_by_username(access_key)
|
fixed_user = FixedUser.get_by_username(access_key)
|
||||||
if fixed_user:
|
if fixed_user:
|
||||||
if secret_key != fixed_user.password:
|
if FixedUser.pass_hashed():
|
||||||
raise errors.unauthorized.InvalidCredentials('bad username or password')
|
if not compare_secret_key_hash(secret_key, fixed_user.password):
|
||||||
|
raise errors.unauthorized.InvalidCredentials(
|
||||||
|
"bad username or password"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
if secret_key != fixed_user.password:
|
||||||
|
raise errors.unauthorized.InvalidCredentials(
|
||||||
|
"bad username or password"
|
||||||
|
)
|
||||||
|
|
||||||
if fixed_user.is_guest and not FixedUser.is_guest_endpoint(service, action):
|
if fixed_user.is_guest and not FixedUser.is_guest_endpoint(service, action):
|
||||||
raise errors.unauthorized.InvalidCredentials('endpoint not allowed for guest')
|
raise errors.unauthorized.InvalidCredentials(
|
||||||
|
"endpoint not allowed for guest"
|
||||||
|
)
|
||||||
|
|
||||||
query = Q(id=fixed_user.user_id)
|
query = Q(id=fixed_user.user_id)
|
||||||
|
|
||||||
with TimingContext("mongo", "user_by_cred"), translate_errors_context('authorizing request'):
|
with TimingContext("mongo", "user_by_cred"), translate_errors_context(
|
||||||
|
"authorizing request"
|
||||||
|
):
|
||||||
user = User.objects(query).first()
|
user = User.objects(query).first()
|
||||||
if not user:
|
if not user:
|
||||||
raise errors.unauthorized.InvalidCredentials('failed to locate provided credentials')
|
raise errors.unauthorized.InvalidCredentials(
|
||||||
|
"failed to locate provided credentials"
|
||||||
|
)
|
||||||
|
|
||||||
if not fixed_user:
|
if not fixed_user:
|
||||||
# In case these are proper credentials, update last used time
|
# In case these are proper credentials, update last used time
|
||||||
@ -87,13 +104,18 @@ def authorize_credentials(auth_data, service, action, call_data_items):
|
|||||||
)
|
)
|
||||||
|
|
||||||
with TimingContext("mongo", "company_by_id"):
|
with TimingContext("mongo", "company_by_id"):
|
||||||
company = Company.objects(id=user.company).only('id', 'name').first()
|
company = Company.objects(id=user.company).only("id", "name").first()
|
||||||
|
|
||||||
if not company:
|
if not company:
|
||||||
raise errors.unauthorized.InvalidCredentials('invalid user company')
|
raise errors.unauthorized.InvalidCredentials("invalid user company")
|
||||||
|
|
||||||
identity = Identity(user=user.id, company=user.company, role=user.role,
|
identity = Identity(
|
||||||
user_name=user.name, company_name=company.name)
|
user=user.id,
|
||||||
|
company=user.company,
|
||||||
|
role=user.role,
|
||||||
|
user_name=user.name,
|
||||||
|
company_name=company.name,
|
||||||
|
)
|
||||||
|
|
||||||
basic = Basic(user_key=access_key, identity=identity)
|
basic = Basic(user_key=access_key, identity=identity)
|
||||||
|
|
||||||
@ -110,3 +132,13 @@ def authorize_impersonation(user, identity, service, action, call):
|
|||||||
raise errors.unauthorized.InvalidCredentials("invalid user company")
|
raise errors.unauthorized.InvalidCredentials("invalid user company")
|
||||||
|
|
||||||
return Payload(auth_type=None, identity=identity)
|
return Payload(auth_type=None, identity=identity)
|
||||||
|
|
||||||
|
|
||||||
|
def compare_secret_key_hash(secret_key: str, hashed_secret: str) -> bool:
|
||||||
|
"""
|
||||||
|
Compare hash for the passed secret key with the passed hash
|
||||||
|
:return: True if equal. Otherwise False
|
||||||
|
"""
|
||||||
|
return bcrypt.checkpw(
|
||||||
|
secret_key.encode(), base64.b64decode(hashed_secret.encode("ascii"))
|
||||||
|
)
|
||||||
|
|||||||
@ -32,6 +32,10 @@ class FixedUser:
|
|||||||
def guest_enabled(cls):
|
def guest_enabled(cls):
|
||||||
return cls.enabled() and config.get("services.auth.fixed_users.guest.enabled", False)
|
return cls.enabled() and config.get("services.auth.fixed_users.guest.enabled", False)
|
||||||
|
|
||||||
|
@classmethod
|
||||||
|
def pass_hashed(cls):
|
||||||
|
return config.get("apiserver.auth.fixed_users.pass_hashed", False)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def validate(cls):
|
def validate(cls):
|
||||||
if not cls.enabled():
|
if not cls.enabled():
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user