Skip to content

Commit 35546d7

Browse files
Merge pull request #44 from d4rkstar/issue_91
feat(actions): added secrets system action
2 parents 728654d + dde3008 commit 35546d7

File tree

7 files changed

+826
-5
lines changed

7 files changed

+826
-5
lines changed

actions/login/__main__.py

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -51,9 +51,21 @@ def build_error(message: str):
5151
def build_response(user_data):
5252
body = {}
5353
envs = list(user_data['env'])
54+
if 'userenv' in user_data:
55+
userenv = list(user_data['userenv'])
56+
else :
57+
userenv = []
5458

59+
# handle user envs
60+
for env in userenv:
61+
body[env['key']] = env['value']
62+
63+
# handle system envs
5564
for env in envs:
56-
body[env['key']]=env['value']
65+
if not env['key'] in body:
66+
body[env['key']]=env['value']
67+
else:
68+
body[f"SYSTEM_{env['key']}"] = env['value']
5769

5870
return {
5971
"statusCode": 200,
@@ -70,6 +82,8 @@ def map_data(user_data):
7082

7183
if 'env' in user_data:
7284
resp['env'] = user_data['env']
85+
if 'userenv' in user_data:
86+
resp['userenv'] = user_data['userenv']
7387

7488
if 'quota' in user_data:
7589
resp['quota'] = user_data['quota']
@@ -90,7 +104,6 @@ def main(args):
90104

91105
if user_data:
92106
if bu.verify_password(password, user_data['password']):
93-
# if(password == user_data['password']):
94107
return build_response(map_data(user_data))
95108
else:
96109
return build_error(f"password mismatch for user {login}")

actions/secrets/__main__.py

Lines changed: 204 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,204 @@
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+
19+
import json
20+
import logging
21+
22+
import nuvolaris.config as cfg
23+
import nuvolaris.couchdb_util as cu
24+
25+
USER_META_DBN = "users_metadata"
26+
27+
28+
def fetch_user_data(db, login: str):
29+
logging.info(f"searching for user {login} data")
30+
try:
31+
selector = {"selector": {"login": {"$eq": login}}}
32+
response = db.find_doc(USER_META_DBN, json.dumps(selector))
33+
34+
if response['docs']:
35+
docs = list(response['docs'])
36+
if len(docs) > 0:
37+
return docs[0]
38+
39+
logging.warning(f"Nuvolaris metadata for user {login} not found!")
40+
return None
41+
except Exception as e:
42+
logging.error(f"failed to query Nuvolaris metadata for user {login}. Reason: {e}")
43+
return None
44+
45+
46+
def save_user_data(db, user_data):
47+
logging.info(f"saving user_data for user {user_data['login']}")
48+
try:
49+
response = db.update_doc(USER_META_DBN, user_data)
50+
return response
51+
except Exception as e:
52+
logging.error(f"failed to update user_data for user {user_data['login']}. Reason: {e}")
53+
return False
54+
55+
56+
def handle_additional_env(user_data, env) -> [dict, list, list, list]:
57+
"""
58+
Merge the old env and return new metadata
59+
60+
Keyword arguments:
61+
key -- the key where merge the old and new env
62+
"""
63+
old_env = env_to_dict(user_data, "userenv")
64+
65+
# Identify added keys (exist in env but not in old_env)
66+
added_keys = [k for k in env if k not in old_env and env[k]]
67+
68+
# Identify changed keys (exist in both but have different values)
69+
changed_keys = [k for k in env if k in old_env and old_env[k] != env[k] and env[k]]
70+
71+
# merge old env and new env
72+
new_env = {
73+
# processed then: remove keys that doesn't exists in second dict
74+
**{k: v for k, v in old_env.items() if k not in env or env[k]},
75+
# processed first: keep keys if have a value
76+
**{k: v for k, v in env.items() if v}
77+
}
78+
79+
# Identify removed keys (exist in old_env but not in new_env)
80+
removed_keys = [k for k in old_env if k not in new_env]
81+
82+
user_data["userenv"] = dict_to_env(new_env)
83+
return user_data, added_keys, removed_keys, changed_keys
84+
85+
86+
def map_data(user_data):
87+
"""
88+
Map the internal nuvolaris user_data records to the auth response
89+
"""
90+
resp = {}
91+
resp['login'] = user_data['login']
92+
resp['email'] = user_data['email']
93+
94+
if 'env' in user_data:
95+
resp['env'] = user_data['env']
96+
97+
if 'userenv' in user_data:
98+
resp['userenv'] = user_data['userenv']
99+
100+
if 'quota' in user_data:
101+
resp['quota'] = user_data['quota']
102+
103+
return resp
104+
105+
106+
def env_to_dict(user_data, key="env"):
107+
"""
108+
extract env from user_data and return it as a dict
109+
110+
Keyword arguments:
111+
key -- the key to extract the env from
112+
"""
113+
body = {}
114+
if key in user_data:
115+
envs = list(user_data[key])
116+
else:
117+
envs = []
118+
119+
for env in envs:
120+
body[env['key']] = env['value']
121+
122+
return body
123+
124+
125+
def dict_to_env(env):
126+
"""
127+
converts an env to a key/pair suitable for user_data storage
128+
"""
129+
body = []
130+
for key in env:
131+
body.append({"key": key, "value": env[key]})
132+
133+
return body
134+
135+
136+
def build_response(user_data: dict, added: list = None, removed: list = None, changed: list = None):
137+
sysenv = env_to_dict(user_data, "env")
138+
userenv = env_to_dict(user_data, "userenv")
139+
140+
body = {"env": {}, "sysenv": {}}
141+
body['env'].update(sysenv)
142+
body['env'].update(userenv)
143+
body['added'] = added
144+
body['removed'] = removed
145+
body['changed'] = changed
146+
for key in sysenv:
147+
if key in userenv:
148+
body['sysenv'][key] = sysenv[key]
149+
150+
return {
151+
"statusCode": 200,
152+
"body": body
153+
}
154+
155+
156+
def build_error(message: str, status_code: int = 400):
157+
return {
158+
"statusCode": status_code,
159+
"body": message
160+
}
161+
162+
163+
def main(args):
164+
cfg.clean()
165+
cfg.put("couchdb.host", args['couchdb_host'])
166+
cfg.put("couchdb.admin.user", args['couchdb_user'])
167+
cfg.put("couchdb.admin.password", args['couchdb_password'])
168+
169+
# Access the headers
170+
headers = args.get('__ow_headers', {})
171+
# Normalize header keys to lowercase
172+
normalized_headers = {key.lower(): value for key, value in headers.items()}
173+
174+
# Example: Get a specific header, e.g., "Authorization"
175+
auth_header = normalized_headers.get('authorization', None)
176+
if auth_header is None:
177+
return build_error("missing authorization header", 401)
178+
179+
if 'login' in args:
180+
db = cu.CouchDB()
181+
login = args['login']
182+
# retrieve user data for the login
183+
user_data = fetch_user_data(db, login)
184+
added = removed = []
185+
186+
if user_data:
187+
# convert them in a dict
188+
envs = env_to_dict(user_data)
189+
190+
# check authorization
191+
if auth_header != envs['AUTH']:
192+
return build_error("invalid authorization header", 401)
193+
194+
if 'env' in args:
195+
user_data, added, removed, changed = handle_additional_env(user_data, args['env'])
196+
if not save_user_data(db, user_data):
197+
return build_error(f"unable to save metadata for user {login}")
198+
199+
return build_response(map_data(user_data), added, removed, changed)
200+
else:
201+
return build_error(f"no user {login} found")
202+
else:
203+
return build_error("please provide login parameters")
204+

0 commit comments

Comments
 (0)