Skip to content

Commit 18fdf31

Browse files
jendruskAlessio Fabiani
andauthored
Added roles management for user (#27)
* Services before refactor * Added services settings * Disabled create_service function * Part of changes - faced problem with geoserver - potential issue * Added setting self and master passwords * Settings simple implementation * Settings Subclass implementation - seems working needs tests * Settings full implementation * Added create user method * Fixed href to delete users * Minor fixes in users * Added roles management for user * Added roles management for user * Fixed serialize * Fixed accept rewrite * Cleaned data printing on save * - Test fixes * - Test fixes * - Test fixes Co-authored-by: Alessio Fabiani <[email protected]>
1 parent 301be5d commit 18fdf31

File tree

4 files changed

+157
-17
lines changed

4 files changed

+157
-17
lines changed

src/geoserver/catalog.py

Lines changed: 65 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -227,7 +227,6 @@ def delete(self, config_object, purge=None, recurse=False):
227227
"Content-type": "application/xml",
228228
"Accept": "application/xml"
229229
}
230-
231230
resp = self.http_request(rest_url, method='delete', headers=headers)
232231
if resp.status_code != 200:
233232
raise FailedRequestError(f'Failed to make DELETE request: {resp.status_code}, {resp.text}')
@@ -1332,11 +1331,42 @@ def get_services(self, ogc_type="wms"):
13321331
data = self.get_xml(f"{self.service_url}/services/{ogc_type}/workspaces/{ws.name}/settings")
13331332
services.append(service_from_index(self, data))
13341333
except FailedRequestError as e:
1335-
logger.debug(f"Not found {ogc_type} service for workspace {ws.name}"
1336-
)
1337-
1334+
logger.debug(f"Not found {ogc_type} service for workspace {ws.name}")
13381335
return services
13391336

1337+
def create_user(self, username, password):
1338+
1339+
users = self.get_users(names=username)
1340+
if len(users) > 0:
1341+
logging.warning(f"User {username} already exists")
1342+
tmp_cat = Catalog(service_url=self.service_url, username=username, password=password)
1343+
try:
1344+
tmp_cat.get_version()
1345+
except FailedRequestError as e:
1346+
logger.error("And we probably have incorrect password")
1347+
raise FailedRequestError
1348+
1349+
return users[0]
1350+
1351+
xml = (
1352+
"<user>"
1353+
"<userName>{username}</userName>"
1354+
"<password>{password}</password>"
1355+
"<enabled>true</enabled>"
1356+
"</user>"
1357+
).format(username=username, password=password)
1358+
1359+
headers = {"Content-Type": "application/xml"}
1360+
users_url = f"{self.service_url}/security/usergroup/users/"
1361+
1362+
resp = self.http_request(users_url, method='post', data=xml, headers=headers)
1363+
if resp.status_code not in (200, 201, 202):
1364+
raise FailedRequestError(f'Failed to create user {username} : {resp.status_code}, {resp.text}')
1365+
1366+
self._cache.pop(f"{self.service_url}/security/usergroup/users/", None)
1367+
users = self.get_users(names=username)
1368+
return users[0] if users else None
1369+
13401370
def get_users(self, names=None):
13411371
'''
13421372
Returns a list of users in the catalog.
@@ -1354,8 +1384,7 @@ def get_users(self, names=None):
13541384
users.extend([user_from_index(self, node) for node in data.findall("user")])
13551385

13561386
if users and names:
1357-
return ([ws for ws in users if ws.name in names])
1358-
1387+
return ([ws for ws in users if ws.user_name in names])
13591388
return users
13601389

13611390
def get_master_pwd(self):
@@ -1411,3 +1440,33 @@ def set_my_pwd(self, new_pwd):
14111440

14121441
def get_global_settings(self):
14131442
return GlobalSettings(self)
1443+
1444+
def get_roles(self):
1445+
url = f"{self.service_url}/security/roles"
1446+
resp = self.get_xml(rest_url=url)
1447+
roles = [x.text for x in resp.findall("role")]
1448+
return roles
1449+
1450+
def get_roles_user(self, username):
1451+
url = f"{self.service_url}/security/roles/user/{username}"
1452+
resp = self.get_xml(rest_url=url)
1453+
roles = [x.text for x in resp.findall("role")]
1454+
return roles
1455+
1456+
def add_role_user(self, rolename, username):
1457+
url = f"{self.service_url}/security/roles/role/{rolename}/user/{username}"
1458+
resp = self.http_request(url, method="post")
1459+
1460+
if resp.status_code != 200:
1461+
raise FailedRequestError(resp.content)
1462+
1463+
self._cache.clear()
1464+
1465+
def del_role_user(self, rolename, username):
1466+
url = f"{self.service_url}/security/roles/role/{rolename}/user/{username}"
1467+
resp = self.http_request(url, method="delete")
1468+
1469+
if resp.status_code != 200:
1470+
raise FailedRequestError(resp.content)
1471+
1472+
self._cache.clear()

src/geoserver/settings.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -112,7 +112,8 @@ def __init__(self, dom):
112112
"pngAcceleration": write_bool("pngAcceleration"),
113113
"jpegAcceleration": write_bool("jpegAcceleration"),
114114
"allowNativeMosaic": write_bool("allowNativeMosaic"),
115-
"allowNativeWarp": write_bool("allowNativeWarp")}
115+
"allowNativeWarp": write_bool("allowNativeWarp")
116+
}
116117

117118

118119
class CoverageAccess(StaticResourceInfo):

src/geoserver/support.py

Lines changed: 13 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -274,15 +274,19 @@ def serialize(self, builder):
274274
self.dirty['advertised'] = self.advertised
275275

276276
for k, writer in self.writers.items():
277-
278-
attr = getattr(self, k)
279-
280-
if issubclass(type(attr), StaticResourceInfo) and attr.dirty:
281-
attr.serialize_all(builder)
282-
else:
283-
if k in self.dirty or self.write_all:
284-
val = self.dirty[k] if self.dirty.get(k) else attr
285-
writer(builder, val)
277+
if hasattr(self, k) and issubclass(type(getattr(self, k)), StaticResourceInfo):
278+
attr = getattr(self, k)
279+
if attr.dirty:
280+
attr.serialize_all(builder)
281+
elif k in self.dirty:
282+
val = self.dirty[k]
283+
writer(builder, val)
284+
elif self.write_all:
285+
attr = None
286+
elif self.write_all and hasattr(self, k):
287+
attr = getattr(self, k)
288+
val = self.dirty[k] if self.dirty.get(k) else attr
289+
writer(builder, val)
286290

287291
def serialize_all(self, builder):
288292
builder.start(self.resource_type, dict())

test/securitytests.py

Lines changed: 77 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
import subprocess
88
import re
99
import time
10-
from geoserver.catalog import Catalog
10+
from geoserver.catalog import Catalog, FailedRequestError
11+
from geoserver.security import User
1112

1213
if GSPARAMS['GEOSERVER_HOME']:
1314
dest = GSPARAMS['DATA_DIR']
@@ -74,6 +75,7 @@
7475

7576

7677
class SecurityTests(unittest.TestCase):
78+
7779
def setUp(self):
7880
self.cat = Catalog(GSPARAMS['GSURL'], username=GSPARAMS['GSUSER'], password=GSPARAMS['GSPASSWORD'])
7981
self.bkp_cat = Catalog(GSPARAMS['GSURL'], username=GSPARAMS['GSUSER'], password=GSPARAMS['GSPASSWORD'])
@@ -84,6 +86,12 @@ def setUp(self):
8486
def tearDown(self) -> None:
8587
self.bkp_cat.set_master_pwd(self.bkp_masterpwd)
8688
self.bkp_cat.set_my_pwd(self.bkp_my_pwd)
89+
usrs = [x for x in self.cat.get_users(names="test_usr12")]
90+
if len(usrs) > 0:
91+
try:
92+
self.cat.delete(usrs[0])
93+
except FailedRequestError as e:
94+
print(f"User DELETE endpoint not available! {e}")
8795

8896
def test_get_users(self):
8997
users = self.cat.get_users()
@@ -107,3 +115,71 @@ def test_set_my_pwd(self):
107115
self.assertIsNotNone(new_pwd)
108116
self.assertEqual(new_pwd, test_pwd)
109117
self.assertEqual(self.cat.password, test_pwd)
118+
119+
def test_create_user(self):
120+
test_user = User(user_name='test_usr12', catalog=self.cat)
121+
test_pass = 'test_pas12'
122+
users = self.cat.get_users(names=test_user.user_name)
123+
if len(users) > 0:
124+
try:
125+
self.cat.delete(test_user)
126+
except FailedRequestError as e:
127+
print(f"User DELETE endpoint not available! {e}")
128+
users = self.cat.get_users(names=test_user.user_name)
129+
self.assertEqual(len(users), 0, msg="Test user exists and I cant delete it")
130+
self.cat.create_user(username=test_user.user_name, password=test_pass)
131+
132+
users = self.cat.get_users(names=test_user.user_name)
133+
self.assertEqual(len(users), 1, msg="Test user was not created")
134+
135+
tmp_cat = Catalog(service_url=self.cat.service_url, username=test_user.user_name, password=test_pass)
136+
try:
137+
tmp_cat.get_users()
138+
except Exception as e:
139+
print(f"Some problem with new user, exc: {e}")
140+
141+
def test_create_existing_user(self):
142+
test_user = User(user_name='test_usr12', catalog=self.cat)
143+
test_pass = 'test_pas12'
144+
self.cat.create_user(username=test_user.user_name, password=test_pass)
145+
users = self.cat.get_users(names=test_user.user_name)
146+
self.assertEqual(len(users), 1, msg="User creation before test failed")
147+
self.cat.create_user(username=test_user.user_name, password=test_pass)
148+
149+
150+
class RolesTests(unittest.TestCase):
151+
152+
def setUp(self) -> None:
153+
self.cat = Catalog(GSPARAMS['GSURL'], username=GSPARAMS['GSUSER'], password=GSPARAMS['GSPASSWORD'])
154+
self.test_usr = "test_usr_role"
155+
self.test_pwd = "test_usr_role"
156+
157+
def tearDown(self) -> None:
158+
usr = User(catalog=self.cat, user_name=self.test_usr)
159+
if usr:
160+
try:
161+
self.cat.delete(usr)
162+
except FailedRequestError as e:
163+
print(f"User DELETE endpoint not available! {e}")
164+
165+
def test_get_all_roles(self):
166+
roles = self.cat.get_roles()
167+
self.assertGreater(len(roles), 0)
168+
self.assertIn("ADMIN", roles)
169+
170+
def test_get_roles_user(self):
171+
roles = self.cat.get_roles_user(username="admin")
172+
self.assertGreater(len(roles), 0)
173+
self.assertIn("ADMIN", roles)
174+
175+
def test_add_del_role_user(self):
176+
self.cat.create_user(username=self.test_usr, password=self.test_pwd)
177+
self.cat.add_role_user(rolename="ADMIN", username=self.test_usr)
178+
usr_roles = self.cat.get_roles_user(username=self.test_usr)
179+
self.assertIn("ADMIN", usr_roles)
180+
tmp_cat = Catalog(service_url=self.cat.service_url, username=self.test_usr, password=self.test_pwd)
181+
# If role added user should get response for this
182+
tmp_cat.get_users()
183+
self.cat.del_role_user(rolename="ADMIN", username=self.test_usr)
184+
usr_roles = self.cat.get_roles_user(username=self.test_usr)
185+
self.assertNotIn("ADMIN", usr_roles)

0 commit comments

Comments
 (0)