This is an automated email from the ASF dual-hosted git repository.
madhan pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/ranger.git
The following commit(s) were added to refs/heads/master by this push:
new 79d49af3f RANGER-3643 : Service config UI to include additional
configurations like service.admin.users, service.admin.groups
79d49af3f is described below
commit 79d49af3f1ae587318f1f9c8f2d9accca6479fdd
Author: Mugdha Varadkar <[email protected]>
AuthorDate: Wed Jul 16 01:27:31 2025 +0530
RANGER-3643 : Service config UI to include additional configurations like
service.admin.users, service.admin.groups
Signed-off-by: Madhan Neethiraj <[email protected]>
---
.../react-webapp/src/components/CreatableField.jsx | 2 +-
.../main/webapp/react-webapp/src/utils/XAEnums.js | 38 ++++++
.../src/views/SecurityZone/SecurityZoneForm.jsx | 18 +--
.../views/ServiceManager/ServiceAuditFilter.jsx | 5 +-
.../src/views/ServiceManager/ServiceForm.jsx | 138 +++++++++++++++++++--
.../views/ServiceManager/ServiceViewDetails.jsx | 101 +++++++++------
6 files changed, 244 insertions(+), 58 deletions(-)
diff --git
a/security-admin/src/main/webapp/react-webapp/src/components/CreatableField.jsx
b/security-admin/src/main/webapp/react-webapp/src/components/CreatableField.jsx
index 0b2620d46..443304430 100644
---
a/security-admin/src/main/webapp/react-webapp/src/components/CreatableField.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/components/CreatableField.jsx
@@ -68,7 +68,7 @@ const CreatableField = (props) => {
menuIsOpen={false}
isClearable={false}
isMulti
- placeholder="Type Action Name"
+ placeholder="Type Operations Name"
value={actionValue}
inputValue={actionInputValue}
onChange={(actionValue) => handleChange(actionValue)}
diff --git a/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js
b/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js
index b79099a95..ac9bdfb30 100755
--- a/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js
+++ b/security-admin/src/main/webapp/react-webapp/src/utils/XAEnums.js
@@ -948,3 +948,41 @@ export const statusClassMap = {
ACTIVE: "badge bg-primary",
DENIED: "badge bg-danger"
};
+
+export const additionalServiceConfigs = [
+ {
+ label: "Policy Download Users",
+ name: "policy.download.auth.users",
+ type: "user"
+ },
+ {
+ label: "Tag Download Users",
+ name: "tag.download.auth.users",
+ type: "user"
+ },
+ {
+ label: "Service Admin Users",
+ name: "service.admin.users",
+ type: "user"
+ },
+ {
+ label: "Service Admin Groups",
+ name: "service.admin.groups",
+ type: "group"
+ },
+ {
+ label: "Superusers",
+ name: "ranger.plugin.super.users",
+ type: "user"
+ },
+ {
+ label: "Superuser Groups",
+ name: "ranger.plugin.super.groups",
+ type: "group"
+ },
+ {
+ label: "Userstore Download Users",
+ name: "userstore.download.auth.users",
+ type: "user"
+ }
+];
diff --git
a/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/SecurityZoneForm.jsx
b/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/SecurityZoneForm.jsx
index 447431e33..a849c6897 100644
---
a/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/SecurityZoneForm.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/views/SecurityZone/SecurityZoneForm.jsx
@@ -873,7 +873,7 @@ const SecurityZoneForm = () => {
IndicatorSeparator: () => null
}}
isClearable={true}
- placeholder="Select User"
+ placeholder="Select Users"
/>
</Col>
</Row>
@@ -917,7 +917,7 @@ const SecurityZoneForm = () => {
IndicatorSeparator: () => null
}}
isClearable={true}
- placeholder="Select Group"
+ placeholder="Select Groups"
/>
</Col>
</Row>
@@ -961,7 +961,7 @@ const SecurityZoneForm = () => {
IndicatorSeparator: () => null
}}
isClearable={true}
- placeholder="Select Role"
+ placeholder="Select Roles"
/>
{meta.touched && meta.error && (
<span className="invalid-field">
@@ -1010,7 +1010,7 @@ const SecurityZoneForm = () => {
IndicatorSeparator: () => null
}}
isClearable={true}
- placeholder="Select User"
+ placeholder="Select Users"
/>
</Col>
</Row>
@@ -1054,7 +1054,7 @@ const SecurityZoneForm = () => {
IndicatorSeparator: () => null
}}
isClearable={true}
- placeholder="Select Group"
+ placeholder="Select Groups"
/>
</Col>
</Row>
@@ -1098,7 +1098,7 @@ const SecurityZoneForm = () => {
IndicatorSeparator: () => null
}}
isClearable={true}
- placeholder="Select Role"
+ placeholder="Select Roles"
/>
{meta.error && meta.touched && (
<span className="invalid-field">
@@ -1117,7 +1117,7 @@ const SecurityZoneForm = () => {
<Row className="form-group">
<Col xs={3}>
<label className="form-label float-end">
- Select Tag Services
+ Tag Services
</label>
</Col>
<Col xs={6}>
@@ -1152,7 +1152,7 @@ const SecurityZoneForm = () => {
<Row className="form-group">
<Col xs={3}>
<label className="form-label float-end">
- Select Resource Services
+ Resource Services
</label>
</Col>
<Col xs={6}>
@@ -1175,7 +1175,7 @@ const SecurityZoneForm = () => {
options={resourceServicesOpt}
isClearable={false}
isSearchable={true}
- placeholder="Select Service Name"
+ placeholder="Select Resource Services"
styles={selectInputCustomStyles}
/>
</Col>
diff --git
a/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceAuditFilter.jsx
b/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceAuditFilter.jsx
index ee57fff2e..36d0d6aa1 100644
---
a/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceAuditFilter.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceAuditFilter.jsx
@@ -281,7 +281,7 @@ export default function ServiceAuditFilter(props) {
}
]}
menuPlacement="auto"
- placeholder="Select Value"
+ placeholder="Select Access Result"
/>
</div>
)}
@@ -428,6 +428,7 @@ export default function ServiceAuditFilter(props) {
cacheOptions
isMulti
styles={selectInputCustomStyles}
+ placeholder="Select Roles"
/>
</div>
)}
@@ -467,6 +468,7 @@ export default function ServiceAuditFilter(props) {
cacheOptions
isMulti
styles={selectInputCustomStyles}
+ placeholder="Select Groups"
/>
</div>
)}
@@ -506,6 +508,7 @@ export default function ServiceAuditFilter(props) {
cacheOptions
isMulti
styles={selectInputCustomStyles}
+ placeholder="Select Users"
/>
</div>
)}
diff --git
a/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceForm.jsx
b/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceForm.jsx
index 44580c32a..7d462d8d6 100644
---
a/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceForm.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceForm.jsx
@@ -25,7 +25,7 @@ import arrayMutators from "final-form-arrays";
import { FieldArray } from "react-final-form-arrays";
import Select from "react-select";
import AsyncSelect from "react-select/async";
-import { RegexValidation } from "Utils/XAEnums";
+import { RegexValidation, additionalServiceConfigs } from "Utils/XAEnums";
import { fetchApi } from "Utils/fetchAPI";
import ServiceAuditFilter from "./ServiceAuditFilter";
import TestConnection from "./TestConnection";
@@ -58,7 +58,10 @@ import {
split,
without,
maxBy,
- cloneDeep
+ cloneDeep,
+ intersection,
+ join,
+ sortBy
} from "lodash";
import withRouter from "Hooks/withRouter";
import { RangerPolicyType } from "Utils/XAEnums";
@@ -78,13 +81,15 @@ class ServiceForm extends Component {
this.state = {
serviceDef: {},
service: {},
- tagService: [],
editInitialValues: {},
showDelete: false,
loader: true,
blockUI: false,
defaultTagOptions: [],
- loadingOptions: false
+ loadingOptions: false,
+ defaultAdditionalUserConfigOptions: [],
+ defaultAdditionalGroupConfigOptions: [],
+ loadingAdditionalConfigOptions: false
};
}
@@ -188,6 +193,15 @@ class ServiceForm extends Component {
}
}
+ for (const config of additionalServiceConfigs) {
+ if (config.name in serviceJson["configs"]) {
+ serviceJson["configs"][config.name] = join(
+ map(serviceJson["configs"][config.name], "value"),
+ ","
+ );
+ }
+ }
+
if (values?.customConfigs !== undefined) {
values.customConfigs?.map((config) => {
config?.name !== undefined &&
@@ -397,10 +411,34 @@ class ServiceForm extends Component {
serviceResp?.data?.configs?.[config];
});
- let editCustomConfigs = serviceCustomConfigs.map((config) => {
- return { name: config, value: serviceResp?.data?.configs[config] };
+ const additionalConfigs = intersection(
+ serviceCustomConfigs,
+ map(additionalServiceConfigs, "name")
+ );
+
+ const editAdditionalConfigs = additionalConfigs.map((config) => {
+ return {
+ name: config,
+ value: map(split(serviceResp?.data?.configs[config], ","), (val) => ({
+ label: val,
+ value: val
+ }))
+ };
+ });
+
+ editAdditionalConfigs.map((config) => {
+ serviceJson["configs"][
+ config.name.replaceAll(".", "_").replaceAll("-", "_")
+ ] = config.value;
});
+ let editCustomConfigs = sortBy(
+ difference(serviceCustomConfigs, additionalConfigs)?.map((config) => {
+ return { name: config, value: serviceResp?.data?.configs[config] };
+ }),
+ "name"
+ );
+
serviceJson["customConfigs"] =
editCustomConfigs.length == 0 ? [undefined] : editCustomConfigs;
@@ -453,6 +491,26 @@ class ServiceForm extends Component {
this.setState({ defaultTagOptions: opts, loadingOptions: false });
});
};
+
+ onFocusAdditionalConfigOptions = (type) => {
+ this.setState({ loadingAdditionalConfigOptions: true });
+ if (type === "user") {
+ this.fetchUsers().then((opts) => {
+ this.setState({
+ defaultAdditionalUserConfigOptions: opts,
+ loadingAdditionalConfigOptions: false
+ });
+ });
+ } else {
+ this.fetchGroups().then((opts) => {
+ this.setState({
+ defaultAdditionalGroupConfigOptions: opts,
+ loadingAdditionalConfigOptions: false
+ });
+ });
+ }
+ };
+
deleteService = async (serviceId) => {
this.hideDeleteModal();
try {
@@ -595,6 +653,65 @@ class ServiceForm extends Component {
return [];
};
+ getAdditionalServiceConfigs = () => {
+ const additionalServiceConfigsFormFields = additionalServiceConfigs.map(
+ (additionalConfig, index) => {
+ this.configsJson[additionalConfig.name] = additionalConfig.name
+ .replaceAll(".", "_")
+ .replaceAll("-", "_");
+ return (
+ <Row
+ className="form-group"
+ key={this.configsJson[additionalConfig.name]}
+ >
+ <Col xs={3}>
+ <label className="form-label float-end">
+ {additionalConfig.label}
+ </label>
+ </Col>
+ <Col xs={4}>
+ <Field
+ name={"configs." + this.configsJson[additionalConfig.name]}
+ key={"configs." + additionalConfig.name + index}
+ id={"configs." + additionalConfig.name}
+ data-cy={"configs." + additionalConfig.name}
+ component={this.AsyncSelectField}
+ loadOptions={
+ additionalConfig.type == "user"
+ ? this.fetchUsers
+ : this.fetchGroups
+ }
+ onFocus={() => {
+ this.onFocusAdditionalConfigOptions(additionalConfig.type);
+ }}
+ defaultOptions={
+ additionalConfig.type == "user"
+ ? this.state.defaultAdditionalUserConfigOptions
+ : this.state.defaultAdditionalGroupConfigOptions
+ }
+ placeholder={
+ additionalConfig.type == "user"
+ ? "Select Users"
+ : "Select Groups"
+ }
+ noOptionsMessage={() =>
+ this.state.loadingAdditionalConfigOptions
+ ? "Loading..."
+ : "No options"
+ }
+ isClearable={true}
+ styles={selectInputCustomStyles}
+ isMulti
+ />
+ </Col>
+ </Row>
+ );
+ }
+ );
+
+ return additionalServiceConfigsFormFields;
+ };
+
getServiceConfigs = (serviceDef) => {
if (serviceDef?.configs !== undefined) {
let formField = [];
@@ -1162,7 +1279,7 @@ class ServiceForm extends Component {
<Row className="form-group">
<Col xs={3}>
<label className="form-label float-end">
- Select Tag Service
+ Tag Service
</label>
</Col>
<Col xs={4}>
@@ -1189,10 +1306,11 @@ class ServiceForm extends Component {
<Col xs={12}>
<p className="form-header">Config Properties :</p>
{this.getServiceConfigs(this.state.serviceDef)}
+ {this.getAdditionalServiceConfigs()}
<Row className="form-group">
<Col xs={3}>
<label className="form-label float-end">
- Add New Configurations
+ Add New Custom Configurations
</label>
</Col>
<Col xs={5}>
@@ -1259,8 +1377,8 @@ class ServiceForm extends Component {
onClick={() =>
addItem("customConfigs", undefined)
}
- data-action="addGroup"
- data-cy="addGroup"
+ data-action="addCustomConfigs"
+ data-cy="addCustomConfigs"
>
<i className="fa-fw fa fa-plus"></i>
</Button>
diff --git
a/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceViewDetails.jsx
b/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceViewDetails.jsx
index 5f5728bad..8cb742c29 100644
---
a/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceViewDetails.jsx
+++
b/security-admin/src/main/webapp/react-webapp/src/views/ServiceManager/ServiceViewDetails.jsx
@@ -20,8 +20,19 @@
import React, { useEffect, useState } from "react";
import { fetchApi } from "Utils/fetchAPI";
import { Alert, Row, Col, Table, Badge, Modal, Button } from "react-bootstrap";
-import { difference, isEmpty, keys, map, omit, pick } from "lodash";
+import {
+ difference,
+ isEmpty,
+ keys,
+ map,
+ omit,
+ pick,
+ intersection,
+ find,
+ sortBy
+} from "lodash";
import { ModalLoader } from "Components/CommonComponents";
+import { additionalServiceConfigs } from "Utils/XAEnums";
export const ServiceViewDetails = (props) => {
const { serviceData: service, serviceDefData } = props;
@@ -74,7 +85,18 @@ export const ServiceViewDetails = (props) => {
let serviceConfigsKey = keys(serviceConfigs);
let serviceDefConfigsKey = map(serviceDefConfigs, "name");
- let customConfigsKey = difference(serviceConfigsKey, serviceDefConfigsKey);
+
+ const additionalServiceConfigsKey = intersection(
+ map(additionalServiceConfigs, "name"),
+ serviceConfigsKey
+ );
+
+ const customConfigsKey = sortBy(
+ difference(
+ difference(serviceConfigsKey, serviceDefConfigsKey),
+ additionalServiceConfigsKey
+ )
+ );
serviceDefConfigs?.map(
(config) =>
@@ -82,6 +104,11 @@ export const ServiceViewDetails = (props) => {
serviceConfigs[config.name])
);
+ additionalServiceConfigsKey.map((config) => {
+ configs[find(additionalServiceConfigs, ["name", config]).label] =
+ serviceConfigs[config];
+ });
+
Object.entries(configs)?.map(([key, value]) =>
tableRow.push(
<tr key={key}>
@@ -98,7 +125,7 @@ export const ServiceViewDetails = (props) => {
tableRow.push(
<tr key="custom-configs-title">
<td colSpan="2">
- <b>Add New Configurations :</b>
+ <b>Custom Configurations :</b>
</td>
</tr>
);
@@ -226,59 +253,59 @@ export const ServiceViewDetails = (props) => {
<td className="text-center">
{a.actions !== undefined
? a.actions.map((action) => (
- <h6 key={action}>
- <Badge bg="info">{action}</Badge>
- </h6>
+ <Badge bg="info" className="m-1 text-truncate" key={action}>
+ {action}
+ </Badge>
))
: "--"}
</td>
<td className="text-center">
{a.accessTypes !== undefined && a.accessTypes.length > 0
? a.accessTypes.map((accessType) => (
- <h6 key={accessType}>
- <Badge bg="info">{accessType}</Badge>
- </h6>
+ <Badge
+ bg="info"
+ className="m-1 text-truncate"
+ key={accessType}
+ >
+ {accessType}
+ </Badge>
))
: "--"}
</td>
<td className="text-center">
{a.users !== undefined
? a.users.map((user) => (
- <h6 key={user}>
- <Badge
- bg="info"
- className="m-1 text-truncate more-less-width"
- title={user}
- key={user}
- >
- {user}
- </Badge>
- </h6>
+ <Badge
+ bg="info"
+ className="m-1 text-truncate more-less-width"
+ title={user}
+ key={user}
+ >
+ {user}
+ </Badge>
))
: "--"}
</td>
<td className="text-center">
{a.groups !== undefined
? a.groups.map((group) => (
- <h6 key={group}>
- <Badge
- bg="info"
- className="m-1 text-truncate more-less-width"
- title={group}
- key={group}
- >
- {group}
- </Badge>
- </h6>
+ <Badge
+ bg="info"
+ className="m-1 text-truncate more-less-width"
+ title={group}
+ key={group}
+ >
+ {group}
+ </Badge>
))
: "--"}
</td>
<td className="text-center">
{a.roles !== undefined
? a.roles.map((role) => (
- <h6 key={role}>
- <Badge bg="info">{role}</Badge>
- </h6>
+ <Badge bg="info" key={role}>
+ {role}
+ </Badge>
))
: "--"}
</td>
@@ -306,16 +333,16 @@ export const ServiceViewDetails = (props) => {
<tbody className="service-details">
<tr>
<td className="text-nowrap">Service Name</td>
- <td className="text-break">{serviceData.name}</td>
+ <td className="text-break">{serviceData?.name}</td>
</tr>
<tr>
<td className="text-nowrap">Display Name</td>
- <td className="text-break">{serviceData.displayName}</td>
+ <td
className="text-break">{serviceData?.displayName}</td>
</tr>
<tr>
<td className="text-nowrap">Description</td>
<td className="text-break">
- {serviceData.description
+ {serviceData?.description
? serviceData.description
: "--"}
</td>
@@ -325,7 +352,7 @@ export const ServiceViewDetails = (props) => {
<td>
<h6>
<Badge bg="info">
- {serviceData.isEnabled ? `Enabled` : `Disabled`}
+ {serviceData?.isEnabled ? `Enabled` : `Disabled`}
</Badge>
</h6>
</td>
@@ -333,7 +360,7 @@ export const ServiceViewDetails = (props) => {
<tr>
<td className="text-nowrap">Tag Service</td>
<td className="text-break">
- {serviceData.tagService ? (
+ {serviceData?.tagService ? (
<h6>
<Badge bg="info">{serviceData.tagService}</Badge>
</h6>