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>

Reply via email to