You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@knox.apache.org by pz...@apache.org on 2020/02/10 15:20:51 UTC

[knox] branch master updated: KNOX-2210 - Gateway-level configuration for server-managed Knox token state (#259)

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

pzampino pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/knox.git


The following commit(s) were added to refs/heads/master by this push:
     new 90a623b  KNOX-2210 - Gateway-level configuration for server-managed Knox token state (#259)
90a623b is described below

commit 90a623b1678773bc0dc5ac4491f9fab2afd73312
Author: Phil Zampino <pz...@apache.org>
AuthorDate: Mon Feb 10 10:20:44 2020 -0500

    KNOX-2210 - Gateway-level configuration for server-managed Knox token state (#259)
---
 .../federation/jwt/filter/AbstractJWTFilter.java   |  25 +++-
 .../provider/federation/CommonJWTFilterTest.java   | 130 +++++++++++++++++++++
 .../gateway/config/impl/GatewayConfigImpl.java     |   8 ++
 .../gateway/service/knoxtoken/TokenResource.java   |  24 +++-
 .../knoxtoken/TokenServiceResourceTest.java        | 107 +++++++++++++++--
 .../apache/knox/gateway/config/GatewayConfig.java  |   6 +
 .../org/apache/knox/gateway/GatewayTestConfig.java |   5 +
 7 files changed, 290 insertions(+), 15 deletions(-)

diff --git a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
index 190af6d..25813f4 100644
--- a/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
+++ b/gateway-provider-security-jwt/src/main/java/org/apache/knox/gateway/provider/federation/jwt/filter/AbstractJWTFilter.java
@@ -49,6 +49,7 @@ import org.apache.knox.gateway.audit.api.AuditServiceFactory;
 import org.apache.knox.gateway.audit.api.Auditor;
 import org.apache.knox.gateway.audit.api.ResourceType;
 import org.apache.knox.gateway.audit.log4j.audit.AuditConstants;
+import org.apache.knox.gateway.config.GatewayConfig;
 import org.apache.knox.gateway.filter.AbstractGatewayFilter;
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 import org.apache.knox.gateway.provider.federation.jwt.JWTMessages;
@@ -111,13 +112,35 @@ public abstract class AbstractJWTFilter implements Filter {
       GatewayServices services = (GatewayServices) context.getAttribute(GatewayServices.GATEWAY_SERVICES_ATTRIBUTE);
       if (services != null) {
         authority = services.getService(ServiceType.TOKEN_SERVICE);
-        if (Boolean.valueOf(filterConfig.getInitParameter(TokenStateService.CONFIG_SERVER_MANAGED))) {
+        if (isServerManagedTokenStateEnabled(filterConfig)) {
           tokenStateService = services.getService(ServiceType.TOKEN_STATE_SERVICE);
         }
       }
     }
   }
 
+  protected boolean isServerManagedTokenStateEnabled(FilterConfig filterConfig) {
+    boolean isServerManaged = false;
+
+    // First, check for explicit provider-level configuration
+    String providerParamValue = filterConfig.getInitParameter(TokenStateService.CONFIG_SERVER_MANAGED);
+
+    // If there is no provider-level configuration
+    if (providerParamValue == null || providerParamValue.isEmpty()) {
+      // Fall back to the gateway-level default
+      ServletContext context = filterConfig.getServletContext();
+      if (context != null) {
+        GatewayConfig config = (GatewayConfig) context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
+        isServerManaged = (config != null) && config.isServerManagedTokenStateEnabled();
+      }
+    } else {
+      // Otherwise, apply the provider-level configuration
+      isServerManaged = Boolean.valueOf(providerParamValue);
+    }
+
+    return isServerManaged;
+  }
+
   protected void configureExpectedParameters(FilterConfig filterConfig) {
     expectedIssuer = filterConfig.getInitParameter(JWT_EXPECTED_ISSUER);
     if (expectedIssuer == null) {
diff --git a/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/CommonJWTFilterTest.java b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/CommonJWTFilterTest.java
new file mode 100644
index 0000000..93bd51b
--- /dev/null
+++ b/gateway-provider-security-jwt/src/test/java/org/apache/knox/gateway/provider/federation/CommonJWTFilterTest.java
@@ -0,0 +1,130 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with this
+ * work for additional information regarding copyright ownership. The ASF
+ * licenses this file to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+ * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+ * License for the specific language governing permissions and limitations under
+ * the License.
+ */
+package org.apache.knox.gateway.provider.federation;
+
+import org.apache.knox.gateway.config.GatewayConfig;
+import org.apache.knox.gateway.provider.federation.jwt.filter.AbstractJWTFilter;
+import org.apache.knox.gateway.services.security.token.TokenStateService;
+import org.easymock.EasyMock;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.FilterChain;
+import javax.servlet.FilterConfig;
+import javax.servlet.ServletContext;
+import javax.servlet.ServletException;
+import javax.servlet.ServletRequest;
+import javax.servlet.ServletResponse;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.lang.reflect.Method;
+
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+
+public class CommonJWTFilterTest {
+
+  private AbstractJWTFilter handler;
+
+  @Before
+  public void setUp() {
+    handler = new TestHandler();
+  }
+
+  @After
+  public void tearDown() {
+    handler = null;
+  }
+
+  @Test
+  public void testServerManagedTokenStateEnabledByProviderOverride() throws Exception {
+    // Provider param should override gateway config
+    assertTrue(doTestServerManagedTokenState(false, "true"));
+  }
+
+  @Test
+  public void testServerManagedTokenStateDisabledByProviderOverride() throws Exception {
+    // Provider param should override gateway config
+    assertFalse(doTestServerManagedTokenState(true, "false"));
+  }
+
+  @Test
+  public void testServerManagedTokenStateDisabledWithoutProviderOverride() throws Exception {
+    // Missing provider param override should apply gateway config
+    assertFalse(doTestServerManagedTokenState(false, null));
+  }
+
+  @Test
+  public void testServerManagedTokenStateEnabledWithoutProviderOverride() throws Exception {
+    // Missing provider param override should apply gateway config
+    assertTrue(doTestServerManagedTokenState(true, null));
+  }
+
+  @Test
+  public void testServerManagedTokenStateDisabledWitEmptyProviderOverride() throws Exception {
+    // Empty provider param override should apply gateway config
+    assertFalse(doTestServerManagedTokenState(false, ""));
+  }
+
+  @Test
+  public void testServerManagedTokenStateEnabledWithEmptyProviderOverride() throws Exception {
+    // Empty provider param override should apply gateway config
+    assertTrue(doTestServerManagedTokenState(true, ""));
+  }
+
+  private boolean doTestServerManagedTokenState(final Boolean isEnabledAtGateway, final String providerParamValue)
+      throws Exception {
+
+    GatewayConfig gwConf = EasyMock.createNiceMock(GatewayConfig.class);
+    EasyMock.expect(gwConf.isServerManagedTokenStateEnabled()).andReturn(isEnabledAtGateway).anyTimes();
+    EasyMock.replay(gwConf);
+
+    ServletContext sc = EasyMock.createNiceMock(ServletContext.class);
+    EasyMock.expect(sc.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(gwConf).anyTimes();
+    EasyMock.replay(sc);
+
+    FilterConfig fc = EasyMock.createNiceMock(FilterConfig.class);
+    EasyMock.expect(fc.getInitParameter(TokenStateService.CONFIG_SERVER_MANAGED)).andReturn(providerParamValue).anyTimes();
+    EasyMock.expect(fc.getServletContext()).andReturn(sc).anyTimes();
+    EasyMock.replay(fc);
+
+    Method m = AbstractJWTFilter.class.getDeclaredMethod("isServerManagedTokenStateEnabled", FilterConfig.class);
+    m.setAccessible(true);
+    return (Boolean) m.invoke(handler, fc);
+  }
+
+
+  static final class TestHandler extends AbstractJWTFilter {
+    @Override
+    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
+
+    }
+
+    @Override
+    protected void handleValidationError(HttpServletRequest request, HttpServletResponse response, int status, String error) throws IOException {
+
+    }
+
+    @Override
+    public void destroy() {
+
+    }
+  }
+
+}
diff --git a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
index 36ba1ee..1a0954a 100644
--- a/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
+++ b/gateway-server/src/main/java/org/apache/knox/gateway/config/impl/GatewayConfigImpl.java
@@ -240,6 +240,8 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
   /* property that specifies list of services for which we need to append service name to the X-Forward-Context header */
   public static final String X_FORWARD_CONTEXT_HEADER_APPEND_SERVICES = GATEWAY_CONFIG_FILE_PREFIX + ".xforwarded.header.context.append.servicename";
 
+  private static final String TOKEN_STATE_SERVER_MANAGED = GATEWAY_CONFIG_FILE_PREFIX + ".knox.token.exp.server-managed";
+
   private static final String CLOUDERA_MANAGER_DESCRIPTORS_MONITOR_INTERVAL = GATEWAY_CONFIG_FILE_PREFIX + ".cloudera.manager.descriptors.monitor.interval";
   private static final long DEFAULT_CLOUDERA_MANAGER_DESCRIPTORS_MONITOR_INTERVAL = 30000L;
   private static final String CLOUDERA_MANAGER_ADVANCED_SERVICE_DISCOVERY_CONF_MONITOR_INTERVAL = GATEWAY_CONFIG_FILE_PREFIX + ".cloudera.manager.advanced.service.discovery.config.monitor.interval";
@@ -1109,4 +1111,10 @@ public class GatewayConfigImpl extends Configuration implements GatewayConfig {
   public long getClouderaManagerAdvancedServiceDiscoveryConfigurationMonitoringInterval() {
     return getLong(CLOUDERA_MANAGER_ADVANCED_SERVICE_DISCOVERY_CONF_MONITOR_INTERVAL, DEFAULT_CLOUDERA_MANAGER_ADVANCED_SERVICE_DISCOVERY_CONF_MONITOR_INTERVAL);
   }
+
+  @Override
+  public boolean isServerManagedTokenStateEnabled() {
+    return getBoolean(TOKEN_STATE_SERVER_MANAGED, false);
+  }
+
 }
diff --git a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
index 10c62e0..70cd59d 100644
--- a/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
+++ b/gateway-service-knoxtoken/src/main/java/org/apache/knox/gateway/service/knoxtoken/TokenResource.java
@@ -40,6 +40,7 @@ import javax.ws.rs.core.Context;
 import javax.ws.rs.core.Response;
 
 import org.apache.commons.codec.binary.Base64;
+import org.apache.knox.gateway.config.GatewayConfig;
 import org.apache.knox.gateway.i18n.messages.MessagesFactory;
 import org.apache.knox.gateway.security.SubjectUtils;
 import org.apache.knox.gateway.services.ServiceType;
@@ -158,8 +159,8 @@ public class TokenResource {
       endpointPublicCert = targetEndpointPublicCert;
     }
 
-    // If server-managed token expiration is configured, set the token store service
-    if (Boolean.valueOf(context.getInitParameter(TokenStateService.CONFIG_SERVER_MANAGED))) {
+    // If server-managed token expiration is configured, set the token state service
+    if (isServerManagedTokenStateEnabled()) {
       String topologyName = getTopologyName();
       log.serverManagedTokenStateEnabled(topologyName);
 
@@ -196,6 +197,25 @@ public class TokenResource {
     }
   }
 
+  private boolean isServerManagedTokenStateEnabled() {
+    boolean isServerManaged;
+
+    // First, check for explicit service-level configuration
+    String serviceParamValue = context.getInitParameter(TokenStateService.CONFIG_SERVER_MANAGED);
+
+    // If there is no service-level configuration
+    if (serviceParamValue == null || serviceParamValue.isEmpty()) {
+      // Fall back to the gateway-level default
+      GatewayConfig config = (GatewayConfig) context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE);
+      isServerManaged = (config != null) && config.isServerManagedTokenStateEnabled();
+    } else {
+      // Otherwise, apply the service-level configuration
+      isServerManaged = Boolean.valueOf(serviceParamValue);
+    }
+
+    return isServerManaged;
+  }
+
   @GET
   @Produces({APPLICATION_JSON, APPLICATION_XML})
   public Response doGet() {
diff --git a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
index 9ccee4d..074dfeb 100644
--- a/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
+++ b/gateway-service-knoxtoken/src/test/java/org/apache/knox/gateway/service/knoxtoken/TokenServiceResourceTest.java
@@ -625,9 +625,31 @@ public class TokenServiceResourceTest {
     assertTrue((expiresDate.getTime() - now.getTime()) < 30000L);
   }
 
+
+  @Test
+  public void testTokenRenewal_ServerManagedStateConfiguredAtGatewayOnly() throws Exception {
+    final String caller = "yarn";
+    Response renewalResponse = doTestTokenRenewal(null, true, caller, null, createTestSubject(caller)).getValue();
+    validateSuccessfulRenewalResponse(renewalResponse);
+  }
+
+  @Test
+  public void testTokenRenewal_ServerManagedStateDisabledAtGatewayWithServiceOverride() throws Exception {
+    final String caller = "yarn";
+    Response renewalResponse = doTestTokenRenewal(true, false, caller, null, createTestSubject(caller)).getValue();
+    validateSuccessfulRenewalResponse(renewalResponse);
+  }
+
+  @Test
+  public void testTokenRenewal_ServerManagedStateEnabledAtGatewayWithServiceOverride() throws Exception {
+    final String caller = "yarn";
+    Response renewalResponse = doTestTokenRenewal(false, true, caller, null, createTestSubject(caller)).getValue();
+    validateRenewalResponse(renewalResponse, 400, false, "Token renewal support is not configured");
+  }
+
   @Test
-  public void testTokenRenewal_ServerManagedStateNotConfigured() throws Exception {
-    Response renewalResponse = doTestTokenRenewal(null, null, null);
+  public void testTokenRenewal_ServerManagedStateNotConfiguredAtAll() throws Exception {
+    Response renewalResponse = doTestTokenRenewal(null, null, null, null, null).getValue();
     validateRenewalResponse(renewalResponse, 400, false, "Token renewal support is not configured");
   }
 
@@ -810,13 +832,39 @@ public class TokenServiceResourceTest {
                                                                         final String  renewers,
                                                                         final Long    maxTokenLifetime,
                                                                         final Subject caller) throws Exception {
+    return doTestTokenRenewal(isTokenStateServerManaged,
+                              null,
+                              renewers,
+                              maxTokenLifetime,
+                              caller);
+  }
+
+  /**
+   *
+   * @param serviceLevelConfig true, if server-side token state management should be enabled; Otherwise, false or null.
+   * @param gatewayLevelConfig true, if server-side token state management should be enabled; Otherwise, false or null.
+   * @param renewers           A comma-delimited list of permitted renewer user names
+   * @param maxTokenLifetime   The maximum duration (milliseconds) for a token's lifetime
+   * @param caller             The user name making the request
+   *
+   * @return The Response from the token renewal request
+   *
+   * @throws Exception
+   */
+  private Map.Entry<TestTokenStateService, Response> doTestTokenRenewal(final Boolean serviceLevelConfig,
+                                                                        final Boolean gatewayLevelConfig,
+                                                                        final String  renewers,
+                                                                        final Long    maxTokenLifetime,
+                                                                        final Subject caller) throws Exception {
     return doTestTokenLifecyle(TokenLifecycleOperation.Renew,
-                               isTokenStateServerManaged,
+                               serviceLevelConfig,
+                               gatewayLevelConfig,
                                renewers,
                                maxTokenLifetime,
                                caller);
   }
 
+
   /**
    *
    * @param isTokenStateServerManaged true, if server-side token state management should be enabled; Otherwise, false or null.
@@ -851,36 +899,71 @@ public class TokenServiceResourceTest {
   }
 
   /**
-   * @param operation                 A TokenLifecycleOperation
-   * @param isTokenStateServerManaged true, if server-side token state management should be enabled; Otherwise, false or null.
-   * @param renewers                  A comma-delimited list of permitted renewer user names
-   * @param maxTokenLifetime          The maximum lifetime duration for a token.
-   * @param caller                    The user name making the request
+   * @param operation          A TokenLifecycleOperation
+   * @param serviceLevelConfig true, if server-side token state management should be enabled at the service level;
+   *                           Otherwise, false or null.
+   * @param renewers           A comma-delimited list of permitted renewer user names
+   * @param maxTokenLifetime   The maximum lifetime duration for a token.
+   * @param caller             The user name making the request
    *
    * @return The Response from the token revocation request
    *
    * @throws Exception
    */
   private Map.Entry<TestTokenStateService, Response> doTestTokenLifecyle(final TokenLifecycleOperation operation,
-                                                                         final Boolean                 isTokenStateServerManaged,
+                                                                         final Boolean                 serviceLevelConfig,
                                                                          final String                  renewers,
                                                                          final Long                    maxTokenLifetime,
                                                                          final Subject                 caller) throws Exception {
+    return doTestTokenLifecyle(operation, serviceLevelConfig, null, renewers, maxTokenLifetime, caller);
+  }
+
+  /**
+   * @param operation          A TokenLifecycleOperation
+   * @param serviceLevelConfig true, if server-side token state management should be enabled at the service level;
+   *                           Otherwise, false or null.
+   * @param gatewayLevelConfig true, if server-side token state management should be enabled at the gateway level;
+   *                           Otherwise, false or null.
+   * @param renewers           A comma-delimited list of permitted renewer user names
+   * @param maxTokenLifetime   The maximum lifetime duration for a token.
+   * @param caller             The user name making the request
+   *
+   * @return The Response from the token revocation request
+   *
+   * @throws Exception
+   */
+  private Map.Entry<TestTokenStateService, Response> doTestTokenLifecyle(final TokenLifecycleOperation operation,
+                                                                         final Boolean                 serviceLevelConfig,
+                                                                         final Boolean                 gatewayLevelConfig,
+                                                                         final String                  renewers,
+                                                                         final Long                    maxTokenLifetime,
+                                                                         final Subject                 caller) throws Exception {
+
     ServletContext context = EasyMock.createNiceMock(ServletContext.class);
     EasyMock.expect(context.getInitParameter("knox.token.audiences")).andReturn("recipient1,recipient2");
     EasyMock.expect(context.getInitParameter("knox.token.ttl")).andReturn(String.valueOf(Long.MAX_VALUE));
     EasyMock.expect(context.getInitParameter("knox.token.target.url")).andReturn(null);
     EasyMock.expect(context.getInitParameter("knox.token.client.data")).andReturn(null);
-    if (isTokenStateServerManaged != null) {
+    // Configure the service-level params
+    if (serviceLevelConfig != null) {
       EasyMock.expect(context.getInitParameter("knox.token.exp.server-managed"))
-              .andReturn(String.valueOf(isTokenStateServerManaged));
+              .andReturn(String.valueOf(serviceLevelConfig));
       if (maxTokenLifetime != null) {
-        EasyMock.expect(context.getInitParameter("knox.token.exp.renew-interval")).andReturn(String.valueOf(maxTokenLifetime / 2));
+        EasyMock.expect(context.getInitParameter("knox.token.exp.renew-interval"))
+                .andReturn(String.valueOf(maxTokenLifetime / 2));
         EasyMock.expect(context.getInitParameter("knox.token.exp.max-lifetime")).andReturn(maxTokenLifetime.toString());
       }
     }
     EasyMock.expect(context.getInitParameter("knox.token.renewer.whitelist")).andReturn(renewers);
 
+    // Configure the gateway-level properties
+    GatewayConfig gatewayConfig = EasyMock.createNiceMock(GatewayConfig.class);
+    if (gatewayLevelConfig != null) {
+      EasyMock.expect(gatewayConfig.isServerManagedTokenStateEnabled()).andReturn(gatewayLevelConfig).anyTimes();
+    }
+    EasyMock.replay(gatewayConfig);
+    EasyMock.expect(context.getAttribute(GatewayConfig.GATEWAY_CONFIG_ATTRIBUTE)).andReturn(gatewayConfig).anyTimes();
+
     HttpServletRequest request = EasyMock.createNiceMock(HttpServletRequest.class);
     EasyMock.expect(request.getServletContext()).andReturn(context).anyTimes();
     Principal principal = EasyMock.createNiceMock(Principal.class);
diff --git a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
index 4aa7192..968b0fc 100644
--- a/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
+++ b/gateway-spi/src/main/java/org/apache/knox/gateway/config/GatewayConfig.java
@@ -652,4 +652,10 @@ public interface GatewayConfig {
    * @return the monitoring interval (in milliseconds) of Cloudera Manager advanced service discovery configuration
    */
   long getClouderaManagerAdvancedServiceDiscoveryConfigurationMonitoringInterval();
+
+  /**
+   * @return true, if state for tokens issued by the Knox Token service should be managed by Knox.
+   */
+  boolean isServerManagedTokenStateEnabled();
+
 }
diff --git a/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java b/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
index f7df9a3..6424d61 100644
--- a/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
+++ b/gateway-test-release-utils/src/main/java/org/apache/knox/gateway/GatewayTestConfig.java
@@ -774,4 +774,9 @@ public class GatewayTestConfig extends Configuration implements GatewayConfig {
   public long getClouderaManagerAdvancedServiceDiscoveryConfigurationMonitoringInterval() {
     return 0;
   }
+
+  @Override
+  public boolean isServerManagedTokenStateEnabled() {
+    return false;
+  }
 }