Skip to content

Commit 2bc596e

Browse files
Merge pull request #40 from d4rkstar/main
feat(operator): bcrypt hashed passwords
2 parents 5c36ed8 + 79e3ae7 commit 2bc596e

File tree

14 files changed

+227
-55
lines changed

14 files changed

+227
-55
lines changed

actions/Taskfile.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ tasks:
7373
login:prepare:
7474
- |-
7575
mkdir -p login/nuvolaris
76-
cp ../nuvolaris/config.py ../nuvolaris/couchdb_util.py login/nuvolaris
76+
cp ../nuvolaris/config.py ../nuvolaris/couchdb_util.py ../nuvolaris/bcrypt_util.py login/nuvolaris
7777
- |-
7878
cd login
7979
rm -f ../{{.DEPLOY}}/login.zip

actions/login/__main__.py

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -16,9 +16,12 @@
1616
# under the License.
1717
#
1818

19+
import json
20+
import logging
21+
22+
import nuvolaris.bcrypt_util as bu
1923
import nuvolaris.config as cfg
2024
import nuvolaris.couchdb_util as cu
21-
import logging, json
2225

2326
USER_META_DBN = "users_metadata"
2427

@@ -79,14 +82,15 @@ def main(args):
7982
cfg.put("couchdb.admin.user", args['couchdb_user'])
8083
cfg.put("couchdb.admin.password", args['couchdb_password'])
8184

82-
if('login' in args and 'password' in args):
85+
if 'login' in args and 'password' in args:
8386
db = cu.CouchDB()
8487
login = args['login']
8588
password = args['password']
8689
user_data = fetch_user_data(db,login)
8790

88-
if(user_data):
89-
if(password == user_data['password']):
91+
if user_data:
92+
if bu.verify_password(password, user_data['password']):
93+
# if(password == user_data['password']):
9094
return build_response(map_data(user_data))
9195
else:
9296
return build_error(f"password mismatch for user {login}")

deploy/nuvolaris-permissions/openwhisk-runtimes-cm.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,7 @@ data:
101101
"image": {
102102
"prefix": "apache",
103103
"name": "openserverless-runtime-python",
104-
"tag": "v3.12-2409142109"
104+
"tag": "v3.12-2501141314"
105105
},
106106
"deprecated": false,
107107
"attached": {

nuvolaris/bcrypt_util.py

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
# Licensed to the Apache Software Foundation (ASF) under one
2+
# or more contributor license agreements. See the NOTICE file
3+
# distributed with this work for additional information
4+
# regarding copyright ownership. The ASF licenses this file
5+
# to you under the Apache License, Version 2.0 (the
6+
# "License"); you may not use this file except in compliance
7+
# with the License. You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing,
12+
# software distributed under the License is distributed on an
13+
# "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
14+
# KIND, either express or implied. See the License for the
15+
# specific language governing permissions and limitations
16+
# under the License.
17+
#
18+
import bcrypt
19+
20+
def hash_password(password: str) -> str:
21+
"""
22+
Apply bcrypt hash algorithm to password.
23+
24+
Args:
25+
password (str): Password to hash
26+
27+
Returns:
28+
str: Hashed password
29+
"""
30+
password_bytes = password.encode('utf-8')
31+
salt = bcrypt.gensalt()
32+
hashed_password = bcrypt.hashpw(password_bytes, salt)
33+
return hashed_password.decode('utf-8')
34+
35+
def verify_password(password: str, hashed_password: str) -> bool:
36+
"""
37+
Verify if hashed password matches password.
38+
39+
Args:
40+
password (str): The plain password to verify
41+
hashed_password (str): The saved hashed password
42+
43+
Returns:
44+
bool: True if hashed password matches password. False otherwise
45+
"""
46+
return bcrypt.checkpw(password.encode('utf-8'), hashed_password.encode('utf-8'))
47+

nuvolaris/nuvolaris_metadata.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,16 +17,24 @@
1717
#
1818
import json
1919
import logging
20+
from datetime import datetime
21+
22+
import nuvolaris.bcrypt_util as bu
2023
import nuvolaris.config as cfg
2124
import nuvolaris.util as util
2225

26+
2327
class NuvolarisMetadata:
2428
_data = {}
2529

2630
def __init__(self):
31+
password = cfg.get('nuvolaris.password') or "nuvpassw0rd"
32+
salted_password = bu.hash_password(password)
33+
2734
self._data = {
2835
"login":"nuvolaris",
29-
"password":cfg.get('nuvolaris.password') or "nuvpassw0rd",
36+
"password":salted_password,
37+
"password_timestamp": datetime.now().isoformat(),
3038
"email":cfg.get('nuvolaris.email') or "[email protected]",
3139
"metadata":[]
3240
}

nuvolaris/operator_util.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -104,7 +104,7 @@ def config_from_spec(spec, handler_type = "on_create"):
104104
"""
105105
Initialize the global configuration from the given spec.
106106
:param spec
107-
:param on_resume boolen flag telling if thsi method is called from the on_resume handler
107+
:param on_resume boolean flag telling if this method is called from the on_resume handler
108108
"""
109109
cfg.clean()
110110
cfg.configure(spec)

nuvolaris/user_metadata.py

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,17 +17,25 @@
1717
#
1818
import json
1919
import logging
20-
import nuvolaris.util as util
20+
from datetime import datetime
2121

22+
import nuvolaris.bcrypt_util as bu
23+
import nuvolaris.util as util
2224
from nuvolaris.user_config import UserConfig
2325

26+
2427
class UserMetadata:
2528
_data = {}
2629

2730
def __init__(self, ucfg: UserConfig):
31+
32+
password = ucfg.get('password')
33+
salted_password = bu.hash_password(password)
34+
2835
self._data = {
2936
"login":ucfg.get('namespace'),
30-
"password":ucfg.get('password'),
37+
"password":salted_password,
38+
"password_timestamp": datetime.now().isoformat(),
3139
"email":ucfg.get('email'),
3240
"metadata":[],
3341
"quota":[]

nuvolaris/userdb_util.py

Lines changed: 46 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -15,26 +15,28 @@
1515
# specific language governing permissions and limitations
1616
# under the License.
1717
#
18-
import logging, json
19-
import nuvolaris.config as cfg
18+
import json
19+
import logging
20+
2021
import nuvolaris.couchdb as cdb
2122
import nuvolaris.couchdb_util as couchdb_util
22-
import nuvolaris.user_config as user_config
2323
import nuvolaris.util as util
24-
import nuvolaris.mongodb as mdb
25-
from nuvolaris.user_metadata import UserMetadata
2624
from nuvolaris.nuvolaris_metadata import NuvolarisMetadata
27-
25+
from nuvolaris.user_metadata import UserMetadata
26+
import nuvolaris.bcrypt_util as bu
2827
USER_META_DBN = "users_metadata"
2928

29+
3030
def _add_metadata(db, metadata):
3131
"""
3232
Add a new Openwhisk User metadata entry
3333
"""
3434
res = util.check(db.wait_db_ready(60), "wait_db_ready", True)
35-
return util.check(cdb.update_templated_doc(db, USER_META_DBN, "user_metadata.json", metadata), f"add_metadata {metadata['login']}", res)
36-
37-
def save_user_metadata(user_metadata:UserMetadata):
35+
return util.check(cdb.update_templated_doc(db, USER_META_DBN, "user_metadata.json", metadata),
36+
f"add_metadata {metadata['login']}", res)
37+
38+
39+
def save_user_metadata(user_metadata: UserMetadata):
3840
"""
3941
Add a generic user metadata into the internal CouchDB
4042
"""
@@ -48,7 +50,8 @@ def save_user_metadata(user_metadata:UserMetadata):
4850
logging.error(f"failed to store Nuvolaris metadata for {metadata['login']}. Cause: {e}")
4951
return None
5052

51-
def save_nuvolaris_metadata(nuvolaris_metadata:NuvolarisMetadata):
53+
54+
def save_nuvolaris_metadata(nuvolaris_metadata: NuvolarisMetadata):
5255
"""
5356
Add nuvolaris user metadata into the internal CouchDB
5457
"""
@@ -60,69 +63,72 @@ def save_nuvolaris_metadata(nuvolaris_metadata:NuvolarisMetadata):
6063
return _add_metadata(db, metadata)
6164
except Exception as e:
6265
logging.error(f"failed to store Nuvolaris metadata for {metadata['login']}. Cause: {e}")
63-
return None
66+
return None
67+
6468

6569
def delete_user_metadata(login):
6670
logging.info(f"removing Nuvolaris metadata for user {login}")
6771

6872
try:
6973
db = couchdb_util.CouchDB()
70-
selector = {"selector":{"login": {"$eq": login }}}
74+
selector = {"selector": {"login": {"$eq": login}}}
7175
response = db.find_doc(USER_META_DBN, json.dumps(selector))
7276

73-
if(response['docs']):
74-
docs = list(response['docs'])
75-
if(len(docs) > 0):
76-
doc = docs[0]
77-
logging.info(f"removing user metadata documents {doc['_id']}")
78-
return db.delete_doc(USER_META_DBN,doc['_id'])
79-
77+
if (response['docs']):
78+
docs = list(response['docs'])
79+
if (len(docs) > 0):
80+
doc = docs[0]
81+
logging.info(f"removing user metadata documents {doc['_id']}")
82+
return db.delete_doc(USER_META_DBN, doc['_id'])
83+
8084
logging.warn(f"Nuvolaris metadata for user {login} not found!")
8185
return None
8286
except Exception as e:
8387
logging.error(f"failed to remove Nuvolaris metadata for user {login}. Reason: {e}")
8488
return None
8589

90+
8691
def update_user_metadata_password(login, password):
8792
logging.info(f"updating Nuvolaris password for user {login}")
8893

8994
try:
9095
db = couchdb_util.CouchDB()
91-
selector = {"selector":{"login": {"$eq": login }}}
96+
selector = {"selector": {"login": {"$eq": login}}}
9297
response = db.find_doc(USER_META_DBN, json.dumps(selector))
9398

94-
if(response['docs']):
95-
docs = list(response['docs'])
96-
if(len(docs) > 0):
97-
doc = docs[0]
98-
logging.info(f"updating user credentials {doc['_id']}")
99-
doc['password']=password
100-
return db.update_doc(USER_META_DBN,doc)
101-
99+
if (response['docs']):
100+
docs = list(response['docs'])
101+
if (len(docs) > 0):
102+
doc = docs[0]
103+
logging.info(f"updating user credentials {doc['_id']}")
104+
doc['password'] = bu.hash_password(password)
105+
return db.update_doc(USER_META_DBN, doc)
106+
102107
logging.warn(f"Nuvolaris metadata for user {login} not found!")
103108
return None
104109
except Exception as e:
105110
logging.error(f"failed to update credentials for quota Nuvolaris user {login}. Reason: {e}")
106-
return None
111+
return None
112+
107113

108-
def update_user_metadata_quota(login, quota:list):
114+
def update_user_metadata_quota(login, quota: list):
109115
logging.info(f"updating Nuvolaris quota for user {login}")
110116

111117
try:
112118
db = couchdb_util.CouchDB()
113-
selector = {"selector":{"login": {"$eq": login }}}
119+
selector = {"selector": {"login": {"$eq": login}}}
114120
response = db.find_doc(USER_META_DBN, json.dumps(selector))
115121

116-
if(response['docs']):
117-
docs = list(response['docs'])
118-
if(len(docs) > 0):
119-
doc = docs[0]
120-
logging.info(f"updating user quota {doc['_id']}")
121-
doc['quota']=quota
122-
return db.update_doc(USER_META_DBN,doc)
123-
122+
if (response['docs']):
123+
docs = list(response['docs'])
124+
if (len(docs) > 0):
125+
doc = docs[0]
126+
logging.info(f"updating user quota {doc['_id']}")
127+
doc['quota'] = quota
128+
return db.update_doc(USER_META_DBN, doc)
129+
124130
logging.warn(f"Nuvolaris metadata for user {login} not found!")
125131
return None
126132
except Exception as e:
127133
logging.error(f"failed to update quota for Nuvolaris user {login}. Reason: {e}")
128-
return None
134+
return None

poetry.lock

Lines changed: 39 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

pyproject.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ backoff = "^2.2.1"
3535
psycopg-binary = "^3.1.18"
3636
psycopg = "^3.1.18"
3737
redis = "^5.0.4"
38+
bcrypt = "^4.2.1"
3839

3940
[tool.poetry.dev-dependencies]
4041
ipython = "^7.31.0"

0 commit comments

Comments
 (0)