@SuppressWarnings("unchecked")
  @Override
  protected DirContext getDirContextInstance(
      final @SuppressWarnings("rawtypes") Hashtable environment) throws NamingException {
    environment.put(Context.SECURITY_AUTHENTICATION, "GSSAPI");

    Subject serviceSubject = login();

    final NamingException[] suppressedException = new NamingException[] {null};
    DirContext dirContext =
        Subject.doAs(
            serviceSubject,
            new PrivilegedAction<DirContext>() {

              @Override
              public DirContext run() {
                try {
                  return KerberosLdapContextSource.super.getDirContextInstance(environment);
                } catch (NamingException e) {
                  suppressedException[0] = e;
                  return null;
                }
              }
            });

    if (suppressedException[0] != null) {
      throw suppressedException[0];
    }

    return dirContext;
  }
Example #2
0
  public void startup(final BrokerOptions options) throws Exception {
    _eventLogger = new EventLogger(new SystemOutMessageLogger());

    Subject.doAs(
        SecurityManager.getSystemTaskSubject("Broker"),
        new PrivilegedExceptionAction<Object>() {
          @Override
          public Object run() throws Exception {
            ch.qos.logback.classic.Logger logger =
                (ch.qos.logback.classic.Logger) LoggerFactory.getLogger(Logger.ROOT_LOGGER_NAME);
            if (!logger.iteratorForAppenders().hasNext()) {
              logger.setLevel(Level.ALL);
              logger.setAdditive(true);
            }

            StartupAppender startupAppender = new StartupAppender();
            startupAppender.setContext(logger.getLoggerContext());
            startupAppender.start();
            logger.addAppender(startupAppender);

            try {
              startupImpl(options);
            } catch (RuntimeException e) {
              LOGGER.error("Exception during startup", e);
              startupAppender.logToConsole();
              closeSystemConfigAndCleanUp();
            } finally {
              logger.detachAppender(startupAppender);
              startupAppender.stop();
            }
            return null;
          }
        });
  }
 public byte[] requestServiceTicket(Subject clientSubject, String servicePrincipalName) {
   // For some reason SPNs in the format HTTP/servicehost.domain end up as
   // HTTP/servicehost.domain/hostname. SPNs defined as [email protected] work fine.
   servicePrincipalName = servicePrincipalName.replace('/', '@');
   try {
     Oid krb5Oid = new Oid("1.2.840.113554.1.2.2");
     GSSManager manager = GSSManager.getInstance();
     GSSName serverName =
         manager.createName(servicePrincipalName, GSSName.NT_HOSTBASED_SERVICE, krb5Oid);
     final GSSContext context =
         manager.createContext(serverName, krb5Oid, null, GSSContext.DEFAULT_LIFETIME);
     byte[] serviceTicket =
         Subject.doAs(
             clientSubject,
             new PrivilegedAction<byte[]>() {
               public byte[] run() {
                 byte[] token = new byte[0];
                 // This is a one pass context initialisation.
                 try {
                   context.requestMutualAuth(false);
                   context.requestCredDeleg(false);
                   return context.initSecContext(token, 0, token.length);
                 } catch (GSSException e) {
                   e.printStackTrace();
                   return null;
                 }
               }
             });
     return serviceTicket;
   } catch (GSSException e) {
     e.printStackTrace();
     return null;
   }
 }
Example #4
0
 private void continueWithEstablishedSecurityContext(
     Subject subject,
     final HttpServletRequest request,
     final HttpServletResponse response,
     final FilterChain chain)
     throws IOException, ServletException {
   try {
     Subject.doAs(
         subject,
         new PrivilegedExceptionAction<Object>() {
           @Override
           public Object run() throws Exception {
             chain.doFilter(request, response);
             return null;
           }
         });
   } catch (PrivilegedActionException e) {
     Throwable t = e.getCause();
     if (t instanceof IOException) {
       throw (IOException) t;
     } else if (t instanceof ServletException) {
       throw (ServletException) t;
     } else {
       throw new ServletException(t);
     }
   }
 }
Example #5
0
  /*
   * (non-Javadoc)
   *
   * @see org.eclipse.equinox.app.IApplication#start(org.eclipse.equinox.app.
   * IApplicationContext)
   */
  @Override
  public Object start(IApplicationContext context) throws Exception {

    while (initApp() == false) {
      ConfigUtil config = ConfigUtil.getInstance();
      ConfigDialog configDialog = new ConfigDialog(shell, config);
      if (configDialog.open() == Window.CANCEL) {
        return EXIT_OK;
      }
    }

    // Setup login context
    String configName = Activator.getConfigurationName();
    URL configUrl = Activator.getContext().getBundle().getEntry(JAAS_CONFIG_FILE);
    loginContext = LoginContextFactory.createContext(configName, configUrl);

    Integer result = null;
    final Display display = PlatformUI.createDisplay();
    try {

      // Run app in secure context
      result = (Integer) Subject.doAs(loginContext.getSubject(), getRunAction(display));
    } finally {
      display.dispose();
      loginContext.logout();
    }
    // TBD handle javax.security.auth.login.LoginException

    if (result != null && PlatformUI.RETURN_RESTART == result.intValue()) return EXIT_RESTART;
    return EXIT_OK;
  }
  @Override
  protected EmbeddedCacheManager createCacheManager() throws Exception {
    final ConfigurationBuilder builder = getDefaultStandaloneCacheConfig(true);
    builder
        .indexing()
        .index(Index.LOCAL)
        .addIndexedEntity(TestEntity.class)
        .addProperty("default.directory_provider", "ram")
        .addProperty("lucene_version", "LUCENE_CURRENT")
        .security()
        .authorization()
        .enable()
        .role("admin")
        .role("query")
        .role("noquery");
    return Subject.doAs(
        ADMIN,
        new PrivilegedAction<EmbeddedCacheManager>() {

          @Override
          public EmbeddedCacheManager run() {
            EmbeddedCacheManager ecm =
                TestCacheManagerFactory.createCacheManager(getSecureGlobalConfiguration(), builder);
            ecm.getCache();
            return ecm;
          }
        });
  }
Example #7
0
  public static void authenticate(
      PGStream pgStream,
      String host,
      String user,
      String password,
      String jaasApplicationName,
      String kerberosServerName,
      Logger logger)
      throws IOException, SQLException {
    if (logger.logDebug()) logger.debug(" <=BE AuthenticationReqGSS");

    Object result = null;

    if (jaasApplicationName == null) jaasApplicationName = "pgjdbc";
    if (kerberosServerName == null) kerberosServerName = "postgres";

    try {
      LoginContext lc =
          new LoginContext(jaasApplicationName, new GSSCallbackHandler(user, password));
      lc.login();

      Subject sub = lc.getSubject();
      PrivilegedAction action =
          new GssAction(pgStream, host, user, password, kerberosServerName, logger);
      result = Subject.doAs(sub, action);
    } catch (Exception e) {
      throw new PSQLException(GT.tr("GSS Authentication failed"), PSQLState.CONNECTION_FAILURE, e);
    }

    if (result instanceof IOException) throw (IOException) result;
    else if (result instanceof SQLException) throw (SQLException) result;
    else if (result != null)
      throw new PSQLException(
          GT.tr("GSS Authentication failed"), PSQLState.CONNECTION_FAILURE, (Exception) result);
  }
Example #8
0
  private String getEncodedKerberosTicket(boolean spnego) throws Exception {

    System.setProperty("java.security.auth.login.config", "src/test/resources/kerberos.jaas");
    System.setProperty("org.apache.xml.security.ignoreLineBreaks", "true");

    Oid kerberos5Oid = null;
    if (spnego) {
      kerberos5Oid = new Oid("1.3.6.1.5.5.2");
    } else {
      kerberos5Oid = new Oid("1.2.840.113554.1.2.2");
    }

    GSSManager manager = GSSManager.getInstance();
    GSSName serverName =
        manager.createName("*****@*****.**", GSSName.NT_HOSTBASED_SERVICE);

    GSSContext context =
        manager.createContext(
            serverName.canonicalize(kerberos5Oid), kerberos5Oid, null, GSSContext.DEFAULT_LIFETIME);

    context.requestCredDeleg(true);

    final byte[] token = new byte[0];

    String contextName = "alice";
    LoginContext lc = new LoginContext(contextName);
    lc.login();

    byte[] ticket =
        (byte[]) Subject.doAs(lc.getSubject(), new CreateServiceTicketAction(context, token));
    return Base64.encode(ticket);
  }
 @Override
 public void doFilter(
     final ServletRequest request, final ServletResponse response, final FilterChain chain)
     throws IOException, ServletException {
   HttpServletRequest httpRequest = ((HttpServletRequest) request);
   StringBuffer sourceUrl = httpRequest.getRequestURL();
   String queryString = httpRequest.getQueryString();
   if (queryString != null) {
     sourceUrl.append("?");
     sourceUrl.append(queryString);
   }
   try {
     request.setAttribute(
         AbstractGatewayFilter.SOURCE_REQUEST_URL_ATTRIBUTE_NAME,
         Parser.parse(sourceUrl.toString()));
   } catch (URISyntaxException e) {
     throw new ServletException(e);
   }
   try {
     Subject.doAs(
         subject,
         new PrivilegedExceptionAction<Void>() {
           @Override
           public Void run() throws Exception {
             chain.doFilter(request, response);
             return null;
           }
         });
   } catch (PrivilegedActionException e) {
     throw new ServletException(e);
   }
 }
Example #10
0
 public SaslServerContext(
     final String mech,
     final String serverName,
     final CallbackHandler callback_handler,
     final Map<String, String> props,
     final Subject subject)
     throws SaslException {
   this.subject = subject;
   if (this.subject != null) {
     try {
       server =
           Subject.doAs(
               this.subject,
               new PrivilegedExceptionAction<SaslServer>() {
                 @Override
                 public SaslServer run() throws Exception {
                   return Sasl.createSaslServer(
                       mech, "jgroups", serverName, props, callback_handler);
                 }
               });
     } catch (PrivilegedActionException e) {
       throw (SaslException)
           e.getCause(); // The createSaslServer will only throw this type of exception
     }
   } else {
     server = Sasl.createSaslServer(mech, "jgroups", serverName, props, callback_handler);
   }
 }
  private Object doAs(
      Subject subj, java.security.PrivilegedExceptionAction action, boolean displaySubject)
      throws Exception {
    Object o = null;

    if (_log.isDebugEnabled() && displaySubject) {
      printPrincipalsInSubject(subj);
    }

    /* 1- Retrieve the current Thread's AccessControlContext via
     *    AccessController.getContext
     * 2- Instantiates a new AccessControlContext using the retrieved
     *    context along with a new SubjectDomainCombiner (constructed
     *    using the provided Subject).
     * 3- Finally, invoke AccessController.doPrivileged, passing it
     *    the provided PrivilegedAction, as well as the newly
     * constructed AccessControlContext.
     */
    o = Subject.doAs(subj, action);

    if (_log.isDebugEnabled() && displaySubject) {
      _log.debug("JaasClient. doAs done ");
    }
    return o;
  }
Example #12
0
 protected byte[] evaluateByServer(final byte[] exchange) throws PrivilegedActionException {
   return Subject.doAs(
       serverSubject,
       new PrivilegedExceptionAction<byte[]>() {
         public byte[] run() throws Exception {
           return server.evaluateResponse(exchange);
         }
       });
 }
Example #13
0
 protected byte[] evaluateByClient(final byte[] exchange) throws PrivilegedActionException {
   return Subject.doAs(
       clientSubject,
       new PrivilegedExceptionAction<byte[]>() {
         public byte[] run() throws Exception {
           return client.evaluateChallenge(exchange);
         }
       });
 }
 protected String getPlainTextPassword(final JDBCSettings settings) {
   return Subject.doAs(
       SecurityManager.getSubjectWithAddedSystemRights(),
       new PrivilegedAction<String>() {
         @Override
         public String run() {
           return settings.getPassword();
         }
       });
 }
 @Override
 protected void teardown() {
   Subject.doAs(
       ADMIN,
       new PrivilegedAction<Void>() {
         @Override
         public Void run() {
           QueryAuthorizationTest.super.teardown();
           return null;
         }
       });
 }
 @Override
 protected void clearContent() {
   Subject.doAs(
       ADMIN,
       new PrivilegedAction<Void>() {
         @Override
         public Void run() {
           cacheManager.getCache().clear();
           return null;
         }
       });
 }
 public void run() {
   try {
     Subject server = subjectFactory.getSubjectForHost(getHostName(exchange));
     // The AcceptSecurityContext takes over responsibility for setting the result.
     Subject.doAs(server, new AcceptSecurityContext(result, exchange, challenge));
   } catch (GeneralSecurityException e) {
     e.printStackTrace();
     result.setResult(new AuthenticationResult(null, AuthenticationOutcome.NOT_AUTHENTICATED));
   } catch (PrivilegedActionException e) {
     e.printStackTrace();
     result.setResult(new AuthenticationResult(null, AuthenticationOutcome.NOT_AUTHENTICATED));
   }
 }
 /**
  * Test for the case that UserGroupInformation.getCurrentUser() is called when the
  * AccessControlContext has a Subject associated with it, but that Subject was not created by
  * Hadoop (ie it has no associated User principal)
  */
 @Test
 public void testUGIUnderNonHadoopContext() throws Exception {
   Subject nonHadoopSubject = new Subject();
   Subject.doAs(
       nonHadoopSubject,
       new PrivilegedExceptionAction<Void>() {
         public Void run() throws IOException {
           UserGroupInformation ugi = UserGroupInformation.getCurrentUser();
           assertNotNull(ugi);
           return null;
         }
       });
 }
Example #19
0
 @Test
 public void testRedirectingStringPost() throws Exception {
   Subject s = SSLUtil.createSubject(SSL_CERT, SSL_KEY);
   PrivilegedExceptionAction<Object> p =
       new PrivilegedExceptionAction<Object>() {
         @Override
         public Object run() throws Exception {
           testRedirectingStringPost(baseHttpUrl);
           testRedirectingStringPost(baseHttpsUrl);
           return null;
         }
       };
   Subject.doAs(s, p);
 }
Example #20
0
 /**
  * POST Parameter data to this Job.
  *
  * @param entity The Representation Entity.
  */
 @Post
 public void accept(final Representation entity) {
   Subject subject = getSubject();
   if (subject == null) // anon
   {
     doAccept(entity);
   } else {
     Subject.doAs(
         subject,
         new PrivilegedAction<Object>() {
           public Object run() {
             doAccept(entity);
             return null;
           }
         });
   }
 }
 /**
  * Run the given action as the user, potentially throwing an exception.
  *
  * @param <T> the return type of the run method
  * @param action the method to execute
  * @return the value from the run method
  * @throws IOException if the action throws an IOException
  * @throws Error if the action throws an Error
  * @throws RuntimeException if the action throws a RuntimeException
  * @throws InterruptedException if the action throws an InterruptedException
  * @throws UndeclaredThrowableException if the action throws something else
  */
 public <T> T doAs(PrivilegedExceptionAction<T> action) throws IOException, InterruptedException {
   try {
     return Subject.doAs(subject, action);
   } catch (PrivilegedActionException pae) {
     Throwable cause = pae.getCause();
     if (cause instanceof IOException) {
       throw (IOException) cause;
     } else if (cause instanceof Error) {
       throw (Error) cause;
     } else if (cause instanceof RuntimeException) {
       throw (RuntimeException) cause;
     } else if (cause instanceof InterruptedException) {
       throw (InterruptedException) cause;
     } else {
       throw new UndeclaredThrowableException(pae, "Unknown exception in doAs");
     }
   }
 }
Example #22
0
 private JSONAware handleSecurely(
     final ServletRequestHandler pReqHandler,
     final HttpServletRequest pReq,
     final HttpServletResponse pResp)
     throws IOException, PrivilegedActionException {
   Subject subject = (Subject) pReq.getAttribute(ConfigKey.JAAS_SUBJECT_REQUEST_ATTRIBUTE);
   if (subject != null) {
     return Subject.doAs(
         subject,
         new PrivilegedExceptionAction<JSONAware>() {
           public JSONAware run() throws IOException {
             return pReqHandler.handleRequest(pReq, pResp);
           }
         });
   } else {
     return pReqHandler.handleRequest(pReq, pResp);
   }
 }
  public void testQuery() throws Exception {
    Policy.setPolicy(new SurefireTestingPolicy());
    System.setSecurityManager(new SecurityManager());
    try {
      Subject.doAs(
          QUERY,
          new PrivilegedExceptionAction<Void>() {

            @Override
            public Void run() throws Exception {
              queryTest();
              return null;
            }
          });
    } finally {
      System.setSecurityManager(null);
      Policy.setPolicy(null);
    }
  }
Example #24
0
  public static void main(String[] args) {
    if (args.length != 2) {
      System.out.println("Usage: java demo.sas.KerberosServer <ior_file> <password>");
      System.exit(-1);
    }

    // login - with Kerberos
    LoginContext loginContext = null;
    try {
      JaasTxtCalbackHandler cbHandler = new JaasTxtCalbackHandler();
      cbHandler.setMyPassword(args[1].toCharArray());
      loginContext = new LoginContext("KerberosService", cbHandler);
      loginContext.login();
    } catch (LoginException le) {
      System.out.println("Login error: " + le);
      System.exit(1);
    }
    mySubject = loginContext.getSubject();
    myPrincipal = (Principal) mySubject.getPrincipals().iterator().next();
    System.out.println("Found principal " + myPrincipal.getName());

    // run in privileged mode
    final String[] finalArgs = args;
    try {
      Subject.doAs(
          mySubject,
          new PrivilegedAction() {
            public Object run() {
              try {
                // create application
                KerberosServer app = new KerberosServer(finalArgs);
                app.orb.run();
              } catch (Exception e) {
                System.out.println("Error running program: " + e);
              }
              return null;
            }
          });
    } catch (Exception e) {
      System.out.println("Error running privileged: " + e);
    }
  }
Example #25
0
  public static void performAs(String principal, String keytab, PrivilegedExceptionAction action)
      throws PrivilegedActionException, LoginException {
    LoginContext lc = null;
    try {
      // Authenticate to Kerberos.
      lc = Krb5Login.withKeyTab(principal, keytab);
      lc.login();

      // Assume the identity of the authenticated principal.
      Subject.doAs(lc.getSubject(), action);

    } finally {
      if (lc != null) {
        try {
          lc.logout();
        } catch (LoginException le) {
          ZimbraLog.account.warn("krb5 logout failed", le);
        }
      }
    }
  }
Example #26
0
  private void doit(
      final boolean get, final HttpServletRequest request, final HttpServletResponse response)
      throws ServletException, IOException {
    if (jobUpdater == null) {
      // config error
      response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
      response.setContentType("text/plain");
      PrintWriter w = response.getWriter();
      w.println("servlet is not configured to accept requests");
      w.close();
      return;
    }

    Subject subject = AuthenticationUtil.getSubject(request);
    if (subject == null) {
      processRequest(get, request, response);
    } else {
      try {
        Subject.doAs(
            subject,
            new PrivilegedExceptionAction<Object>() {
              public Object run() throws PrivilegedActionException {
                try {
                  processRequest(get, request, response);
                  return null;
                } catch (Exception ex) {
                  throw new PrivilegedActionException(ex);
                }
              }
            });
      } catch (PrivilegedActionException pex) {
        if (pex.getCause() instanceof ServletException) throw (ServletException) pex.getCause();
        else if (pex.getCause() instanceof IOException) throw (IOException) pex.getCause();
        else if (pex.getCause() instanceof RuntimeException)
          throw (RuntimeException) pex.getCause();
        else throw new RuntimeException(pex.getCause());
      }
    }
  }
Example #27
0
  private void authenticateToken(final byte[] kerberosToken)
      throws AuthLoginException, GSSException, Exception {

    debug.message("In authenticationToken ...");
    Subject.doAs(
        serviceSubject,
        new PrivilegedExceptionAction() {
          public Object run() throws Exception {
            GSSContext context = GSSManager.getInstance().createContext((GSSCredential) null);
            debug.message("Context created.");

            byte[] outToken = context.acceptSecContext(kerberosToken, 0, kerberosToken.length);
            if (outToken != null) {
              if (debug.messageEnabled()) {
                debug.message(
                    "Token returned from acceptSecContext: \n"
                        + DerValue.printByteArray(outToken, 0, outToken.length));
              }
            }
            if (!context.isEstablished()) {
              debug.message("Cannot establish context !");
              throw new AuthLoginException(amAuthWindowsDesktopSSO, "context", null);
            } else {
              debug.message("Context establised !");
              GSSName user = context.getSrcName();
              storeUsernamePasswd(user.toString(), null);

              if (debug.messageEnabled()) {
                debug.message("User authenticated: " + user.toString());
              }
              if (user != null) {
                setPrincipal(user.toString());
              }
            }
            context.dispose();
            return null;
          }
        });
  }
  /**
   * Get Sentry authorized client to communicate with sentry server, the client can be for a
   * minicluster, real distributed cluster, sentry server can use policy file or it's a service.
   *
   * @param clientShortName: principal prefix string
   * @param clientKeyTabDir: authorization key path
   * @return sentry client to talk to sentry server
   * @throws Exception
   */
  public static SentryPolicyServiceClient getSentryClient(
      String clientShortName, String clientKeyTabDir) throws Exception {
    if (!useSentryService) {
      LOGGER.info("Running on a minicluser env.");
      return getSentryClient();
    }

    if (clientKerberos) {
      if (sentryConf == null) {
        sentryConf = new Configuration(false);
      }
      final String SENTRY_HOST = System.getProperty("sentry.host", SERVER_HOST);
      final String SERVER_KERBEROS_PRINCIPAL = "sentry/" + SENTRY_HOST + "@" + REALM;
      sentryConf.set(ServerConfig.PRINCIPAL, SERVER_KERBEROS_PRINCIPAL);
      sentryConf.set(ServerConfig.KEY_TAB, SERVER_KEY_TAB);
      sentryConf.set(ServerConfig.ALLOW_CONNECT, "hive");
      sentryConf.set(ServerConfig.SECURITY_USE_UGI_TRANSPORT, "false");
      sentryConf.set(
          ClientConfig.SERVER_RPC_ADDRESS, System.getProperty("sentry.service.server.rpc.address"));
      sentryConf.set(
          ClientConfig.SERVER_RPC_PORT,
          System.getProperty("sentry.service.server.rpc.port", "8038"));
      sentryConf.set(ClientConfig.SERVER_RPC_CONN_TIMEOUT, "720000"); // millis
      Subject clientSubject = getClientSubject(clientShortName, clientKeyTabDir);
      client =
          Subject.doAs(
              clientSubject,
              new PrivilegedExceptionAction<SentryPolicyServiceClient>() {
                @Override
                public SentryPolicyServiceClient run() throws Exception {
                  return SentryServiceClientFactory.create(sentryConf);
                }
              });
    } else {
      client = getSentryClient();
    }
    return client;
  }
 /**
  * Respond to server's SASL token.
  *
  * @param saslTokenMessage contains server's SASL token
  * @return client's response SASL token
  */
 public byte[] saslResponse(SaslMessageToken saslTokenMessage) {
   try {
     final SaslMessageToken fSaslTokenMessage = saslTokenMessage;
     byte[] retval =
         Subject.doAs(
             subject,
             new PrivilegedExceptionAction<byte[]>() {
               public byte[] run() {
                 try {
                   byte[] retval = saslClient.evaluateChallenge(fSaslTokenMessage.getSaslToken());
                   return retval;
                 } catch (SaslException e) {
                   LOG.error("saslResponse: Failed to respond to SASL server's token:", e);
                   throw new RuntimeException(e);
                 }
               }
             });
     return retval;
   } catch (PrivilegedActionException e) {
     LOG.error("Failed to generate response for token: ", e);
     throw new RuntimeException(e);
   }
 }
  /**
   * Perform the JAAS login and run the command within a privileged scope.
   *
   * @param privilegedSendMessage the PrivilegedSendMessage
   * @return The result Document
   */
  private Document runPrivileged(final PrivilegedSendMessage privilegedSendMessage) {
    final CallbackHandler handler = new ProvidedAuthCallback(username, password);
    Document result;
    try {
      final LoginContext lc =
          new LoginContext("", null, handler, new KerberosJaasConfiguration(kerberosDebug));
      lc.login();

      result = Subject.doAs(lc.getSubject(), privilegedSendMessage);
    } catch (LoginException e) {
      throw new WinRMRuntimeIOException(
          "Login failure sending message on " + getTargetURL() + " error: " + e.getMessage(),
          privilegedSendMessage.getRequestDocument(),
          null,
          e);
    } catch (PrivilegedActionException e) {
      throw new WinRMRuntimeIOException(
          "Failure sending message on " + getTargetURL() + " error: " + e.getMessage(),
          privilegedSendMessage.getRequestDocument(),
          null,
          e.getException());
    }
    return result;
  }