Skip to content

Commit 8dee920

Browse files
fix(ui): show OAuth 2.0 entities on tabs other than "account" (#1895)
**Issue number:** ADDON-82672 ### PR Type **What kind of change does this PR introduce?** * [ ] Feature * [x] Bug Fix * [ ] Refactoring (no functional or API changes) * [ ] Documentation Update * [ ] Maintenance (dependency updates, CI, etc.) ## Summary ### Changes UI had a hardcoded check that showed oauth2 entities only if tab's name was "account". They were hidden if the name was different. ### User experience The flow will work as before for "account" tabs. Users will now be able to add oauth entities on other tabs as well. ## Checklist If an item doesn't apply to your changes, leave it unchecked. ### Review * [x] self-review - I have performed a self-review of this change according to the [development guidelines](https://splunk.github.io/addonfactory-ucc-generator/contributing/#development-guidelines) * [ ] Changes are documented. The documentation is understandable, examples work [(more info)](https://splunk.github.io/addonfactory-ucc-generator/contributing/#documentation-guidelines) * [x] PR title and description follows the [contributing principles](https://splunk.github.io/addonfactory-ucc-generator/contributing/#pull-requests) * [ ] meeting - I have scheduled a meeting or recorded a demo to explain these changes (if there is a video, put a link below and in the ticket) ### Tests See [the testing doc](https://splunk.github.io/addonfactory-ucc-generator/contributing/#build-and-test). * [x] Unit - tests have been added/modified to cover the changes * [x] Smoke - tests have been added/modified to cover the changes * [x] UI - tests have been added/modified to cover the changes * [x] coverage - I have checked the code coverage of my changes [(see more)](https://splunk.github.io/addonfactory-ucc-generator/contributing/#checking-the-code-coverage) **Demo/meeting:** *Reviewers are encouraged to request meetings or demos if any part of the change is unclear*
1 parent 5522137 commit 8dee920

File tree

14 files changed

+359
-9
lines changed

14 files changed

+359
-9
lines changed

tests/smoke/test_ucc_build.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,7 @@ def test_ucc_generate_with_everything(caplog):
182182
("bin", "splunk_ta_uccexample_rh_account.py"),
183183
("bin", "splunk_ta_uccexample_rh_example_input_one.py"),
184184
("bin", "splunk_ta_uccexample_rh_example_input_two.py"),
185+
("bin", "splunk_ta_uccexample_rh_organization.py"),
185186
("bin", "splunk_ta_uccexample_rh_three_custom.py"),
186187
("bin", "splunk_ta_uccexample_rh_example_input_four.py"),
187188
("bin", "splunk_ta_uccexample_custom_rh.py"),
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,167 @@
1+
2+
import import_declare_test
3+
4+
from splunktaucclib.rest_handler.endpoint import (
5+
field,
6+
validator,
7+
RestModel,
8+
SingleModel,
9+
)
10+
from splunktaucclib.rest_handler import admin_external, util
11+
from splunktaucclib.rest_handler.admin_external import AdminExternalHandler
12+
import logging
13+
import json
14+
from solnlib import splunk_rest_client as rest_client
15+
from splunk.admin import InternalException
16+
17+
18+
util.remove_http_proxy_env_vars()
19+
20+
21+
special_fields = [
22+
field.RestField(
23+
'name',
24+
required=True,
25+
encrypted=False,
26+
default=None,
27+
validator=None
28+
)
29+
]
30+
31+
fields = [
32+
field.RestField(
33+
'client_id',
34+
required=False,
35+
encrypted=False,
36+
default=None,
37+
validator=None
38+
),
39+
field.RestField(
40+
'client_secret',
41+
required=False,
42+
encrypted=True,
43+
default=None,
44+
validator=None
45+
),
46+
field.RestField(
47+
'redirect_url',
48+
required=False,
49+
encrypted=False,
50+
default=None,
51+
validator=None
52+
),
53+
field.RestField(
54+
'access_token',
55+
required=False,
56+
encrypted=True,
57+
default=None,
58+
validator=None
59+
),
60+
field.RestField(
61+
'refresh_token',
62+
required=False,
63+
encrypted=True,
64+
default=None,
65+
validator=None
66+
),
67+
field.RestField(
68+
'instance_url',
69+
required=False,
70+
encrypted=False,
71+
default=None,
72+
validator=None
73+
)
74+
]
75+
model = RestModel(fields, name=None, special_fields=special_fields)
76+
77+
78+
endpoint = SingleModel(
79+
'splunk_ta_uccexample_organization',
80+
model,
81+
config_name='organization',
82+
need_reload=False,
83+
)
84+
85+
APP_NAME = 'Splunk_TA_UCCExample'
86+
OAUTH_ENDPOINT = 'splunk_ta_uccexample_oauth'
87+
TOKEN_ENDPOINT = '/services/oauth2/token'
88+
89+
90+
class HandlerWithOauth(AdminExternalHandler):
91+
def __init__(self, *args, **kw):
92+
super().__init__(*args, **kw)
93+
self._oauth_url = f"/servicesNS/nobody/{APP_NAME}/{OAUTH_ENDPOINT}/oauth"
94+
self._rest_client = rest_client.SplunkRestClient(
95+
self.getSessionKey(),
96+
app=APP_NAME,
97+
)
98+
99+
def oauth_call_url(self):
100+
host = (
101+
self.callerArgs.data.get("endpoint_token_oauth_credentials", [None])[0]
102+
or self.callerArgs.data.get("endpoint_token", [None])[0]
103+
or self.callerArgs.data.get("endpoint", [None])[0]
104+
)
105+
106+
return f"https://{host}/{TOKEN_ENDPOINT.lstrip('/')}"
107+
108+
def oauth_client_credentials_call(self):
109+
auth_type = self.callerArgs.data.get("auth_type", [""])[0]
110+
if auth_type != "oauth_client_credentials":
111+
return
112+
113+
client_id = (
114+
self.callerArgs.data.get("client_id_oauth_credentials", [None])[0]
115+
or self.callerArgs.data.get("client_id", [None])[0]
116+
)
117+
118+
client_secret = (
119+
self.callerArgs.data.get("client_secret_oauth_credentials", [None])[0]
120+
or self.callerArgs.data.get("client_secret", [None])[0]
121+
)
122+
123+
params = {
124+
"grant_type": "client_credentials",
125+
"client_id": client_id,
126+
"client_secret": client_secret,
127+
"url": self.oauth_call_url(),
128+
"method": "POST",
129+
}
130+
131+
if "scope" in self.callerArgs.data:
132+
params["scope"] = self.callerArgs.data.get("scope", [None])[0]
133+
134+
data = json.loads(
135+
self._rest_client.post(
136+
self._oauth_url,
137+
body=params,
138+
headers=[("Content-Type", "application/json")],
139+
output_mode="json",
140+
).body.read().decode("utf-8")
141+
)["entry"][0]["content"]
142+
143+
if "access_token" not in data:
144+
data = data.get("error", data)
145+
raise InternalException("Error while trying to obtain OAuth token: %s" % data)
146+
147+
self.payload["access_token"] = data["access_token"]
148+
149+
for key in ["refresh_token", "instance_url"]:
150+
if key in data:
151+
self.payload[key] = data[key]
152+
153+
def handleCreate(self, confInfo):
154+
self.oauth_client_credentials_call()
155+
return super().handleCreate(confInfo)
156+
157+
def handleEdit(self, confInfo):
158+
self.oauth_client_credentials_call()
159+
return super().handleEdit(confInfo)
160+
161+
162+
if __name__ == '__main__':
163+
logging.getLogger().addHandler(logging.NullHandler())
164+
admin_external.handle(
165+
endpoint,
166+
handler=HandlerWithOauth,
167+
)

tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/app.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,3 +29,4 @@ reload.splunk_ta_uccexample_custom_row_tab = simple
2929
reload.splunk_ta_uccexample_tab_hidden_for_cloud = simple
3030
reload.splunk_ta_uccexample_tab_hidden_for_enterprise = simple
3131
reload.splunk_ta_uccexample_oauth = simple
32+
reload.splunk_ta_uccexample_organization = simple

tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/restmap.conf

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[admin:splunk_ta_uccexample_configuration]
22
match = /
3-
members = splunk_ta_uccexample_account, splunk_ta_uccexample_custom_amd_row_tab, splunk_ta_uccexample_custom_row_tab, splunk_ta_uccexample_oauth, splunk_ta_uccexample_settings, splunk_ta_uccexample_tab_hidden_for_cloud, splunk_ta_uccexample_tab_hidden_for_enterprise
3+
members = splunk_ta_uccexample_account, splunk_ta_uccexample_custom_amd_row_tab, splunk_ta_uccexample_custom_row_tab, splunk_ta_uccexample_oauth, splunk_ta_uccexample_organization, splunk_ta_uccexample_settings, splunk_ta_uccexample_tab_hidden_for_cloud, splunk_ta_uccexample_tab_hidden_for_enterprise
44
capability.put = admin_all_objects
55
capability.post = list_storage_passwords
66

@@ -32,6 +32,13 @@ handlerfile = splunk_ta_uccexample_rh_account.py
3232
handleractions = edit, list, remove, create
3333
handlerpersistentmode = true
3434

35+
[admin_external:splunk_ta_uccexample_organization]
36+
handlertype = python
37+
python.version = python3
38+
handlerfile = splunk_ta_uccexample_rh_organization.py
39+
handleractions = edit, list, remove, create
40+
handlerpersistentmode = true
41+
3542
[admin_external:splunk_ta_uccexample_custom_row_tab]
3643
handlertype = python
3744
python.version = python3

tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/server.conf

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
[shclustering]
22
conf_replication_include.splunk_ta_uccexample_settings = true
33
conf_replication_include.splunk_ta_uccexample_account = true
4+
conf_replication_include.splunk_ta_uccexample_organization = true
45
conf_replication_include.splunk_ta_uccexample_custom_row_tab = true
56
conf_replication_include.splunk_ta_uccexample_custom_amd_row_tab = true
67
conf_replication_include.splunk_ta_uccexample_tab_hidden_for_cloud = true

tests/testdata/expected_addons/expected_output_global_config_everything/Splunk_TA_UCCExample/default/web.conf

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,14 @@ methods = POST, GET
1414
pattern = splunk_ta_uccexample_account/*
1515
methods = POST, GET, DELETE
1616

17+
[expose:splunk_ta_uccexample_organization]
18+
pattern = splunk_ta_uccexample_organization
19+
methods = POST, GET
20+
21+
[expose:splunk_ta_uccexample_organization_specified]
22+
pattern = splunk_ta_uccexample_organization/*
23+
methods = POST, GET, DELETE
24+
1725
[expose:splunk_ta_uccexample_custom_row_tab]
1826
pattern = splunk_ta_uccexample_custom_row_tab
1927
methods = POST, GET

tests/testdata/test_addons/package_global_config_everything/globalConfig.json

Lines changed: 66 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -693,6 +693,71 @@
693693
"restHandlerModule": "splunk_ta_uccexample_validate_account_rh",
694694
"restHandlerClass": "CustomAccountValidator"
695695
},
696+
{
697+
"name": "organization",
698+
"title": "Organization",
699+
"table": {
700+
"actions": [
701+
"edit",
702+
"delete",
703+
"clone"
704+
],
705+
"header": [
706+
{
707+
"label": "Name",
708+
"field": "name"
709+
},
710+
{
711+
"label": "Auth Type",
712+
"field": "auth_type"
713+
}
714+
]
715+
},
716+
"entity": [
717+
{
718+
"type": "text",
719+
"label": "Name",
720+
"field": "name",
721+
"help": "Enter a unique name for this account.",
722+
"required": true
723+
},
724+
{
725+
"type": "oauth",
726+
"field": "oauth",
727+
"label": "Not used",
728+
"options": {
729+
"auth_type": [
730+
"oauth"
731+
],
732+
"oauth": [
733+
{
734+
"oauth_field": "client_id",
735+
"label": "Client Id",
736+
"field": "client_id",
737+
"help": "Enter the Client Id for this account."
738+
},
739+
{
740+
"oauth_field": "client_secret",
741+
"label": "Client Secret",
742+
"field": "client_secret",
743+
"encrypted": true,
744+
"help": "Enter the Client Secret key for this account."
745+
},
746+
{
747+
"oauth_field": "redirect_url",
748+
"label": "Redirect url",
749+
"field": "redirect_url",
750+
"help": "Copy and paste this URL into your app."
751+
}
752+
],
753+
"auth_code_endpoint": "/services/oauth2/authorize",
754+
"access_token_endpoint": "/services/oauth2/token",
755+
"oauth_timeout": 30,
756+
"oauth_state_enabled": false
757+
}
758+
}
759+
]
760+
},
696761
{
697762
"name": "custom_tab",
698763
"title": "Custom_tab without entity",
@@ -2407,7 +2472,7 @@
24072472
"meta": {
24082473
"name": "Splunk_TA_UCCExample",
24092474
"restRoot": "splunk_ta_uccexample",
2410-
"version": "5.69.0+d6b58f8c0",
2475+
"version": "5.69.1+d31a2b981",
24112476
"displayName": "Splunk UCC test Add-on",
24122477
"schemaVersion": "0.0.9",
24132478
"supportedThemes": [

tests/ui/pages/account_page.py

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -131,12 +131,14 @@ def __init__(
131131
ucc_smartx_selenium_helper=None,
132132
ucc_smartx_rest_helper=None,
133133
open_page=True,
134+
name="account",
134135
):
135136
"""
136137
:param ucc_smartx_selenium_helper: smartx configuration fixture
137138
"""
139+
self.name = name
138140
super().__init__(ucc_smartx_selenium_helper, ucc_smartx_rest_helper, open_page)
139-
account_container = Selector(select='div[id="accountTab"]')
141+
account_container = Selector(select=f'div[id="{name}Tab"]')
140142

141143
if ucc_smartx_selenium_helper:
142144
self.table = ConfigurationTable(
@@ -170,10 +172,10 @@ def open(self):
170172
f"{self.splunk_web_url}/en-US/app/{C.ADDON_NAME}/configuration"
171173
)
172174
tab = Tab(self.browser)
173-
tab.open_tab("account")
175+
tab.open_tab(self.name)
174176

175177
def _get_account_endpoint(self):
176178
"""
177179
Get rest endpoint for the configuration
178180
"""
179-
return f"{self.splunk_mgmt_url}/servicesNS/nobody/{C.ADDON_NAME}/splunk_ta_uccexample_account"
181+
return f"{self.splunk_mgmt_url}/servicesNS/nobody/{C.ADDON_NAME}/splunk_ta_uccexample_{self.name}"

tests/ui/test_configuration_page_account_tab.py

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -442,6 +442,21 @@ def test_account_oauth_fields_label_entity(
442442
self.assert_util(account.entity.client_secret.get_input_label, "Client Secret")
443443
self.assert_util(account.entity.redirect_url.get_input_label, "Redirect url")
444444

445+
@pytest.mark.execute_enterprise_cloud_true
446+
@pytest.mark.forwarder
447+
@pytest.mark.account
448+
def test_account_oauth_fields_different_tab(
449+
self, ucc_smartx_selenium_helper, ucc_smartx_rest_helper
450+
):
451+
"""Verifies oauth account field label"""
452+
account = AccountPage(
453+
ucc_smartx_selenium_helper, ucc_smartx_rest_helper, name="organization"
454+
)
455+
account.entity.open()
456+
self.assert_util(account.entity.client_id.get_input_label, "Client Id")
457+
self.assert_util(account.entity.client_secret.get_input_label, "Client Secret")
458+
self.assert_util(account.entity.redirect_url.get_input_label, "Redirect url")
459+
445460
@pytest.mark.execute_enterprise_cloud_true
446461
@pytest.mark.account
447462
def test_account_help_text_entity(

ui/src/components/BaseFormView/BaseFormView.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -251,7 +251,7 @@ class BaseFormView extends PureComponent<BaseFormProps, BaseFormState> {
251251
this.entities?.forEach((e) => {
252252
if (e.type === 'oauth') {
253253
this.isOAuth = true;
254-
if (props.page === PAGE_CONF && props.serviceName === 'account') {
254+
if (props.page === PAGE_CONF) {
255255
const authType: Array<AvaillableOAuthTypes> = e?.options?.auth_type;
256256
this.isoauthState =
257257
typeof e?.options?.oauth_state_enabled !== 'undefined'

0 commit comments

Comments
 (0)