This is an automated email from the ASF dual-hosted git repository.

ramackri 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 7017225e6 RANGER-5654: Fix Solr audit dispatcher Kerberos TGT relogin 
(No key to store) via keytab-aware JAAS relogin (#1030)
7017225e6 is described below

commit 7017225e604c3506b52db1d87c44325794204056
Author: Ramachandran Krishnan <[email protected]>
AuthorDate: Wed Jun 24 16:25:42 2026 +0530

    RANGER-5654: Fix Solr audit dispatcher Kerberos TGT relogin (No key to 
store) via keytab-aware JAAS relogin (#1030)
    
    * RANGER-5654:Solr audit dispatcher fails to index after Kerberos TGT 
relogin (No key to store) with default useTicketCache=true
    
    Co-authored-by: Cursor <[email protected]>
    
    * RANGER-5654: Restore site XML descriptions and document relogin recovery
    
    Co-authored-by: Cursor <[email protected]>
    
    * RANGER-5654: Drop AbstractKerberosUser change; config-only fix
    
    Co-authored-by: Cursor <[email protected]>
    
    * RANGER-5654: Keytab-aware JAAS relogin; restore useTicketCache=true
    
    Replace config-only useTicketCache=false with in-place keytab relogin in
    AbstractKerberosUser (agents-audit + security-admin). KerberosAction uses
    performRelogin() on SecurityException retry. Revert shipped and docker site
    XML to useTicketCache=true so the Java fix alone addresses "No key to store"
    at TGT renewal. Add unit test for keytab relogin without logout.
    
    Co-authored-by: Cursor <[email protected]>
    
    * RANGER-5654: Wrap Kerberos javadoc and log lines for checkstyle
    
    Keep LineLength within 80 characters in AbstractKerberosUser,
    KerberosAction, and KerberosJAASConfigUser; no new checkstyle
    violations versus the pre-change baseline in those files.
    
    Co-authored-by: Cursor <[email protected]>
    
    * RANGER-5654: Use single-line Kerberos relogin log statements
    
    Co-authored-by: Cursor <[email protected]>
    
    * RANGER-5654: Address KerberosJAASConfigUser PR review feedback
    
    Cache useKeyTab in the constructor, rename helper to getBooleanOption,
    and use a single return per method in both agents-audit and security-admin.
    
    ---------
    
    Co-authored-by: ramk <[email protected]>
    Co-authored-by: Cursor <[email protected]>
---
 .../ranger/audit/utils/AbstractKerberosUser.java   | 76 ++++++++++++++++++++--
 .../apache/ranger/audit/utils/KerberosAction.java  | 27 ++++++--
 .../ranger/audit/utils/KerberosJAASConfigUser.java | 38 ++++++++++-
 .../ranger/solr/krb/AbstractKerberosUser.java      | 76 ++++++++++++++++++++--
 .../org/apache/ranger/solr/krb/KerberosAction.java | 30 +++++++--
 .../ranger/solr/krb/KerberosJAASConfigUser.java    | 37 ++++++++++-
 .../ranger/solr/krb/AbstractKerberosUserTest.java  | 34 ++++++++++
 7 files changed, 293 insertions(+), 25 deletions(-)

diff --git 
a/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/AbstractKerberosUser.java
 
b/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/AbstractKerberosUser.java
index 47d16f31a..b78d64d5b 100644
--- 
a/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/AbstractKerberosUser.java
+++ 
b/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/AbstractKerberosUser.java
@@ -152,9 +152,16 @@ public <T> T doAs(final PrivilegedExceptionAction<T> 
action) throws IllegalState
     }
 
     /**
-     * Re-login a user from keytab if TGT is expired or is close to expiry.
+     * Proactively renew credentials when the TGT is missing or has passed
+     * {@link #TICKET_RENEW_WINDOW} (80%) of its lifetime.
      *
-     * @throws LoginException if an error happens performing the re-login
+     * <p>Called on every {@link KerberosAction#execute()} before the 
privileged
+     * action runs, so long-lived Solr/Kafka dispatchers refresh before the TGT
+     * expires.
+     *
+     * @return {@code true} if relogin was performed, {@code false} if the TGT
+     *         is still valid
+     * @throws LoginException if relogin fails
      */
     @Override
     public synchronized boolean checkTGTAndRelogin() throws LoginException {
@@ -172,10 +179,62 @@ public synchronized boolean checkTGTAndRelogin() throws 
LoginException {
 
         LOG.debug("Performing relogin for {}", getPrincipal());
 
+        performRelogin();
+
+        return true;
+    }
+
+    /**
+     * Renews JAAS credentials for proactive TGT refresh and for error 
recovery.
+     *
+     * <p>With {@code useKeyTab=true} and {@code useTicketCache=true}
+     * (shipped Solr dispatcher default), the previous {@code logout(); 
login()}
+     * sequence at the 80% TGT renewal window leaves {@code Krb5LoginModule}
+     * with no encryption key for the ticket cache and fails with
+     * {@code "No key to store"}, stopping audit indexing until process 
restart.
+     * For keytab-backed users, calling {@code loginContext.login()} on the
+     * existing {@link LoginContext} and {@link Subject} renews from the keytab
+     * without clearing credentials first. The {@link Subject} is preserved
+     * across relogin because other components may hold references to it.
+     *
+     * <p>Non-keytab principals, or callers that are not yet logged in, use the
+     * traditional {@code logout(); login()} path. In-place keytab relogin
+     * failures also fall back to that path.
+     *
+     * @throws LoginException if relogin fails
+     * @see KerberosJAASConfigUser#useKeytabRelogin()
+     */
+    public synchronized void performRelogin() throws LoginException {
+        if (useKeytabRelogin() && isLoggedIn() && loginContext != null) {
+            try {
+                // In-place keytab relogin without logout() 
(useTicketCache=true).
+                loginContext.login();
+                loggedIn.set(true);
+
+                LOG.debug("Successful in-place keytab relogin for {}", 
getPrincipal());
+
+                return;
+            } catch (LoginException e) {
+                LOG.warn("In-place keytab relogin failed for {}, falling back 
to logout/login: {}", getPrincipal(), e.getMessage());
+            }
+        }
+
         logout();
         login();
+    }
 
-        return true;
+    /**
+     * Whether {@link #performRelogin()} should renew from keytab in place (no
+     * {@code logout()} first). Default {@code false};
+     * {@link KerberosJAASConfigUser} returns {@code true} when JAAS
+     * {@code useKeyTab} is enabled. Gated on keytab capability, not on
+     * {@code useTicketCache}, because only keytab principals can safely skip
+     * logout.
+     *
+     * @return {@code true} to use in-place keytab relogin
+     */
+    protected boolean useKeytabRelogin() {
+        return false;
     }
 
     /**
@@ -197,11 +256,18 @@ public String toString() {
     protected abstract LoginContext createLoginContext(Subject subject) throws 
LoginException;
 
     /**
-     * Get the Kerberos TGT.
+     * Returns the Kerberos TGT from the JAAS {@link Subject}, if present.
      *
-     * @return the user's TGT or null if none was found
+     * @return the user's TGT, or {@code null} if the subject is uninitialized
+     *         or has no TGT
      */
     private synchronized KerberosTicket getTGT() {
+        // Subject is created lazily on first login(); null before that is
+        // expected.
+        if (subject == null) {
+            return null;
+        }
+
         final Set<KerberosTicket> tickets = 
subject.getPrivateCredentials(KerberosTicket.class);
 
         for (KerberosTicket ticket : tickets) {
diff --git 
a/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/KerberosAction.java
 
b/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/KerberosAction.java
index 4754b15d1..e53eb077c 100644
--- 
a/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/KerberosAction.java
+++ 
b/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/KerberosAction.java
@@ -45,6 +45,15 @@ public KerberosAction(final KerberosUser kerberosUser, final 
PrivilegedException
         Validate.notNull(this.logger);
     }
 
+    /**
+     * Runs {@code action} as {@code kerberosUser}: lazy login, proactive TGT
+     * refresh at 80% lifetime ({@link KerberosUser#checkTGTAndRelogin()}),
+     * then the privileged action. On {@link SecurityException}, relogin once 
via
+     * {@link AbstractKerberosUser#performRelogin()} (keytab-safe) and retry.
+     *
+     * @return the result of {@code action}
+     * @throws Exception if login, relogin, or the privileged action fails
+     */
     public T execute() throws Exception {
         T result;
 
@@ -59,14 +68,17 @@ public T execute() throws Exception {
             }
         }
 
-        // check if we need to re-login, will only happen if re-login window 
is reached (80% of TGT life)
+        // check if we need to re-login, will only happen if re-login window is
+        // reached (80% of TGT life)
         try {
             kerberosUser.checkTGTAndRelogin();
         } catch (LoginException e) {
             throw new Exception("Relogin check failed due to: " + 
e.getMessage(), e);
         }
 
-        // attempt to execute the action, if an exception is caught attempt to 
logout/login and retry
+        // On SecurityException, relogin and retry once. Use performRelogin()
+        // for AbstractKerberosUser so keytab clients skip logout first; bare
+        // logout/login reproduces "No key to store" when useTicketCache is 
true.
         try {
             result = kerberosUser.doAs(action);
         } catch (SecurityException se) {
@@ -74,12 +86,17 @@ public T execute() throws Exception {
             logger.debug("", se);
 
             try {
-                kerberosUser.logout();
-                kerberosUser.login();
+                if (kerberosUser instanceof AbstractKerberosUser) {
+                    ((AbstractKerberosUser) kerberosUser).performRelogin();
+                } else {
+                    kerberosUser.logout();
+                    kerberosUser.login();
+                }
 
                 result = kerberosUser.doAs(action);
             } catch (Exception e) {
-                throw new Exception("Retrying privileged action failed due to: 
" + e.getMessage(), e);
+                throw new Exception(
+                        "Retrying privileged action failed due to: " + 
e.getMessage(), e);
             }
         } catch (PrivilegedActionException pae) {
             final Exception cause = pae.getException();
diff --git 
a/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/KerberosJAASConfigUser.java
 
b/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/KerberosJAASConfigUser.java
index 149364d4b..92e6a2dfa 100644
--- 
a/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/KerberosJAASConfigUser.java
+++ 
b/agents-audit/core/src/main/java/org/apache/ranger/audit/utils/KerberosJAASConfigUser.java
@@ -34,12 +34,16 @@
 public class KerberosJAASConfigUser extends AbstractKerberosUser {
     private static final Logger LOG = 
LoggerFactory.getLogger(KerberosJAASConfigUser.class);
 
+    private static final String JAAS_USE_KEYTAB = "useKeyTab";
+
     private final String        configName;
     private final Configuration config;
+    private final boolean       optionUseKeyTab;
 
     public KerberosJAASConfigUser(final String configName, final Configuration 
config) {
-        this.configName = configName;
-        this.config     = config;
+        this.configName      = configName;
+        this.config          = config;
+        this.optionUseKeyTab = getBooleanOption(JAAS_USE_KEYTAB);
     }
 
     @Override
@@ -60,6 +64,36 @@ public String getPrincipal() {
         return ret;
     }
 
+    /**
+     * Solr/Kafka outbound JAAS clients (audit dispatcher, plugin Solr
+     * destination) use {@code useKeyTab=true}. Opt those principals into
+     * in-place keytab relogin so shipped {@code useTicketCache=true} does not
+     * fail at TGT renewal with {@code "No key to store"}.
+     */
+    @Override
+    protected boolean useKeytabRelogin() {
+        return optionUseKeyTab;
+    }
+
+    private boolean getBooleanOption(String optionName) {
+        boolean                 ret     = false;
+        AppConfigurationEntry[] entries = 
config.getAppConfigurationEntry(configName);
+
+        if (entries != null) {
+            for (AppConfigurationEntry entry : entries) {
+                Object value = entry.getOptions().get(optionName);
+
+                if (value != null && Boolean.parseBoolean(value.toString())) {
+                    ret = true;
+
+                    break;
+                }
+            }
+        }
+
+        return ret;
+    }
+
     @Override
     protected LoginContext createLoginContext(Subject subject) throws 
LoginException {
         LOG.debug("==> KerberosJAASConfigUser.createLoginContext()");
diff --git 
a/security-admin/src/main/java/org/apache/ranger/solr/krb/AbstractKerberosUser.java
 
b/security-admin/src/main/java/org/apache/ranger/solr/krb/AbstractKerberosUser.java
index f4bfb5b3f..22f6d1818 100644
--- 
a/security-admin/src/main/java/org/apache/ranger/solr/krb/AbstractKerberosUser.java
+++ 
b/security-admin/src/main/java/org/apache/ranger/solr/krb/AbstractKerberosUser.java
@@ -151,9 +151,16 @@ public <T> T doAs(final PrivilegedExceptionAction<T> 
action) throws IllegalState
     }
 
     /**
-     * Re-login a user from keytab if TGT is expired or is close to expiry.
+     * Proactively renew credentials when the TGT is missing or has passed
+     * {@link #TICKET_RENEW_WINDOW} (80%) of its lifetime.
      *
-     * @throws LoginException if an error happens performing the re-login
+     * <p>Called on every {@link KerberosAction#execute()} before the 
privileged
+     * action runs, so long-lived Solr audit queries refresh before the TGT
+     * expires.
+     *
+     * @return {@code true} if relogin was performed, {@code false} if the TGT
+     *         is still valid
+     * @throws LoginException if relogin fails
      */
     @Override
     public synchronized boolean checkTGTAndRelogin() throws LoginException {
@@ -171,10 +178,62 @@ public synchronized boolean checkTGTAndRelogin() throws 
LoginException {
 
         LOG.info("Performing relogin for {}", getPrincipal());
 
+        performRelogin();
+
+        return true;
+    }
+
+    /**
+     * Renews JAAS credentials for proactive TGT refresh and for error 
recovery.
+     *
+     * <p>With {@code useKeyTab=true} and {@code useTicketCache=true}
+     * (shipped Solr dispatcher default), the previous {@code logout(); 
login()}
+     * sequence at the 80% TGT renewal window leaves {@code Krb5LoginModule}
+     * with no encryption key for the ticket cache and fails with
+     * {@code "No key to store"}, stopping audit indexing until process 
restart.
+     * For keytab-backed users, calling {@code loginContext.login()} on the
+     * existing {@link LoginContext} and {@link Subject} renews from the keytab
+     * without clearing credentials first. The {@link Subject} is preserved
+     * across relogin because other components may hold references to it.
+     *
+     * <p>Non-keytab principals, or callers that are not yet logged in, use the
+     * traditional {@code logout(); login()} path. In-place keytab relogin
+     * failures also fall back to that path.
+     *
+     * @throws LoginException if relogin fails
+     * @see KerberosJAASConfigUser#useKeytabRelogin()
+     */
+    public synchronized void performRelogin() throws LoginException {
+        if (useKeytabRelogin() && isLoggedIn() && loginContext != null) {
+            try {
+                // In-place keytab relogin without logout() 
(useTicketCache=true).
+                loginContext.login();
+                loggedIn.set(true);
+
+                LOG.info("Successful in-place keytab relogin for {}", 
getPrincipal());
+
+                return;
+            } catch (LoginException e) {
+                LOG.warn("In-place keytab relogin failed for {}, falling back 
to logout/login: {}", getPrincipal(), e.getMessage());
+            }
+        }
+
         logout();
         login();
+    }
 
-        return true;
+    /**
+     * Whether {@link #performRelogin()} should renew from keytab in place (no
+     * {@code logout()} first). Default {@code false};
+     * {@link KerberosJAASConfigUser} returns {@code true} when JAAS
+     * {@code useKeyTab} is enabled. Gated on keytab capability, not on
+     * {@code useTicketCache}, because only keytab principals can safely skip
+     * logout.
+     *
+     * @return {@code true} to use in-place keytab relogin
+     */
+    protected boolean useKeytabRelogin() {
+        return false;
     }
 
     /**
@@ -193,11 +252,18 @@ public String toString() {
     protected abstract LoginContext createLoginContext(Subject subject) throws 
LoginException;
 
     /**
-     * Get the Kerberos TGT.
+     * Returns the Kerberos TGT from the JAAS {@link Subject}, if present.
      *
-     * @return the user's TGT or null if none was found
+     * @return the user's TGT, or {@code null} if the subject is uninitialized
+     *         or has no TGT
      */
     private synchronized KerberosTicket getTGT() {
+        // Subject is created lazily on first login(); null before that is
+        // expected.
+        if (subject == null) {
+            return null;
+        }
+
         final Set<KerberosTicket> tickets = 
subject.getPrivateCredentials(KerberosTicket.class);
 
         for (KerberosTicket ticket : tickets) {
diff --git 
a/security-admin/src/main/java/org/apache/ranger/solr/krb/KerberosAction.java 
b/security-admin/src/main/java/org/apache/ranger/solr/krb/KerberosAction.java
index 9ed1d4484..d544175a4 100644
--- 
a/security-admin/src/main/java/org/apache/ranger/solr/krb/KerberosAction.java
+++ 
b/security-admin/src/main/java/org/apache/ranger/solr/krb/KerberosAction.java
@@ -45,6 +45,15 @@ public KerberosAction(final KerberosUser kerberosUser, final 
PrivilegedException
         Validate.notNull(this.logger);
     }
 
+    /**
+     * Runs {@code action} as {@code kerberosUser}: lazy login, proactive TGT
+     * refresh at 80% lifetime ({@link KerberosUser#checkTGTAndRelogin()}),
+     * then the privileged action. On {@link SecurityException}, relogin once 
via
+     * {@link AbstractKerberosUser#performRelogin()} (keytab-safe) and retry.
+     *
+     * @return the result of {@code action}
+     * @throws Exception if login, relogin, or the privileged action fails
+     */
     public T execute() throws Exception {
         T result;
 
@@ -61,14 +70,17 @@ public T execute() throws Exception {
             }
         }
 
-        // check if we need to re-login, will only happen if re-login window 
is reached (80% of TGT life)
+        // check if we need to re-login, will only happen if re-login window is
+        // reached (80% of TGT life)
         try {
             kerberosUser.checkTGTAndRelogin();
         } catch (LoginException e) {
             throw new Exception("Relogin check failed due to: " + 
e.getMessage(), e);
         }
 
-        // attempt to execute the action, if an exception is caught attempt to 
logout/login and retry
+        // On SecurityException, relogin and retry once. Use performRelogin()
+        // for AbstractKerberosUser so keytab clients skip logout first; bare
+        // logout/login reproduces "No key to store" when useTicketCache is 
true.
         try {
             result = kerberosUser.doAs(action);
         } catch (SecurityException se) {
@@ -78,17 +90,23 @@ public T execute() throws Exception {
             }
 
             try {
-                kerberosUser.logout();
-                kerberosUser.login();
+                if (kerberosUser instanceof AbstractKerberosUser) {
+                    ((AbstractKerberosUser) kerberosUser).performRelogin();
+                } else {
+                    kerberosUser.logout();
+                    kerberosUser.login();
+                }
 
                 result = kerberosUser.doAs(action);
             } catch (Exception e) {
-                throw new Exception("Retrying privileged action failed due to: 
" + e.getMessage(), e);
+                throw new Exception(
+                        "Retrying privileged action failed due to: " + 
e.getMessage(), e);
             }
         } catch (PrivilegedActionException pae) {
             final Exception cause = pae.getException();
 
-            throw new Exception("Privileged action failed due to: " + 
cause.getMessage(), cause);
+            throw new Exception(
+                    "Privileged action failed due to: " + cause.getMessage(), 
cause);
         }
 
         return result;
diff --git 
a/security-admin/src/main/java/org/apache/ranger/solr/krb/KerberosJAASConfigUser.java
 
b/security-admin/src/main/java/org/apache/ranger/solr/krb/KerberosJAASConfigUser.java
index c170ab728..c1431477b 100644
--- 
a/security-admin/src/main/java/org/apache/ranger/solr/krb/KerberosJAASConfigUser.java
+++ 
b/security-admin/src/main/java/org/apache/ranger/solr/krb/KerberosJAASConfigUser.java
@@ -34,12 +34,16 @@
 public class KerberosJAASConfigUser extends AbstractKerberosUser {
     private static final Logger LOG = 
LoggerFactory.getLogger(KerberosJAASConfigUser.class);
 
+    private static final String JAAS_USE_KEYTAB = "useKeyTab";
+
     private final String        configName;
     private final Configuration config;
+    private final boolean       optionUseKeyTab;
 
     public KerberosJAASConfigUser(final String configName, final Configuration 
config) {
-        this.configName = configName;
-        this.config     = config;
+        this.configName      = configName;
+        this.config          = config;
+        this.optionUseKeyTab = getBooleanOption(JAAS_USE_KEYTAB);
     }
 
     @Override
@@ -60,6 +64,35 @@ public String getPrincipal() {
         return ret;
     }
 
+    /**
+     * Admin Solr audit queries use {@code useKeyTab=true}. Opt those 
principals
+     * into in-place keytab relogin so shipped {@code useTicketCache=true} does
+     * not fail at TGT renewal with {@code "No key to store"}.
+     */
+    @Override
+    protected boolean useKeytabRelogin() {
+        return optionUseKeyTab;
+    }
+
+    private boolean getBooleanOption(String optionName) {
+        boolean                 ret     = false;
+        AppConfigurationEntry[] entries = 
config.getAppConfigurationEntry(configName);
+
+        if (entries != null) {
+            for (AppConfigurationEntry entry : entries) {
+                Object value = entry.getOptions().get(optionName);
+
+                if (value != null && Boolean.parseBoolean(value.toString())) {
+                    ret = true;
+
+                    break;
+                }
+            }
+        }
+
+        return ret;
+    }
+
     @Override
     protected LoginContext createLoginContext(Subject subject) throws 
LoginException {
         LOG.debug("==> KerberosJAASConfigUser.createLoginContext()");
diff --git 
a/security-admin/src/test/java/org/apache/ranger/solr/krb/AbstractKerberosUserTest.java
 
b/security-admin/src/test/java/org/apache/ranger/solr/krb/AbstractKerberosUserTest.java
index cbd69fd9c..18b4e4c1d 100644
--- 
a/security-admin/src/test/java/org/apache/ranger/solr/krb/AbstractKerberosUserTest.java
+++ 
b/security-admin/src/test/java/org/apache/ranger/solr/krb/AbstractKerberosUserTest.java
@@ -142,6 +142,29 @@ public void 
checkTGTAndRelogin_reloginWhenOnlyNonTGSTicketPresent() throws Excep
         verify(ctx, times(1)).login();
     }
 
+    @Test
+    public void checkTGTAndRelogin_keytabUserSkipsLogoutWhenLoggedIn() throws 
Exception {
+        Subject subject = new Subject();
+        KerberosTicket tgt = mock(KerberosTicket.class);
+        when(tgt.getServer()).thenReturn(new 
KerberosPrincipal("krbtgt/[email protected]"));
+        Date start = new Date(System.currentTimeMillis() - 60 * 60_000);
+        Date end   = new Date(System.currentTimeMillis() + 60_000);
+        when(tgt.getStartTime()).thenReturn(start);
+        when(tgt.getEndTime()).thenReturn(end);
+        subject.getPrivateCredentials().add(tgt);
+
+        LoginContext     ctx  = mock(LoginContext.class);
+        KeytabKerberosUser user = new KeytabKerberosUser("[email protected]", 
subject, ctx);
+
+        user.login();
+        Mockito.reset(ctx);
+
+        boolean relogin = user.checkTGTAndRelogin();
+        assertTrue(relogin);
+        verify(ctx, never()).logout();
+        verify(ctx, times(1)).login();
+    }
+
     private static class TestKerberosUser extends AbstractKerberosUser {
         private final String       principal;
         private final LoginContext ctx;
@@ -162,4 +185,15 @@ protected LoginContext createLoginContext(Subject subject) 
throws LoginException
             return ctx;
         }
     }
+
+    private static class KeytabKerberosUser extends TestKerberosUser {
+        KeytabKerberosUser(String principal, Subject subject, LoginContext 
ctx) {
+            super(principal, subject, ctx);
+        }
+
+        @Override
+        protected boolean useKeytabRelogin() {
+            return true;
+        }
+    }
 }

Reply via email to