Michael Pasternak has uploaded a new change for review.

Change subject: restapi: allow client to define the HTTP session TTL #921013
......................................................................

restapi: allow client to define the HTTP session TTL #921013

Session TTL can be explicitly set by user now via "Session-TTL:xxx"
HTTP header,

Session-TTL is the time between client requests before the servlet
container will invalidate this session. An interval value of zero
or less indicates that the session should never timeout.

https://bugzilla.redhat.com/show_bug.cgi?id=921013

Change-Id: I753a379c60b2d7f88867cd0123ee69321a331f0f
Signed-off-by: Michael Pasternak <mpast...@redhat.com>
---
M 
backend/manager/modules/restapi/interface/common/jaxrs/src/main/java/org/ovirt/engine/api/common/security/auth/Challenger.java
M 
backend/manager/modules/restapi/interface/common/jaxrs/src/main/java/org/ovirt/engine/api/common/security/auth/SessionUtils.java
M 
backend/manager/modules/restapi/interface/common/jaxrs/src/test/java/org/ovirt/engine/api/common/security/auth/ChallengerTest.java
M 
backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd
M 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendApiResource.java
A 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/rsdl/EntryPointBuilder.java
M 
backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/rsdl/RsdlBuilder.java
M backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml
8 files changed, 219 insertions(+), 13 deletions(-)


  git pull ssh://gerrit.ovirt.org:29418/ovirt-engine refs/changes/44/13044/1

diff --git 
a/backend/manager/modules/restapi/interface/common/jaxrs/src/main/java/org/ovirt/engine/api/common/security/auth/Challenger.java
 
b/backend/manager/modules/restapi/interface/common/jaxrs/src/main/java/org/ovirt/engine/api/common/security/auth/Challenger.java
index 8fc9fe1..9cf204d 100644
--- 
a/backend/manager/modules/restapi/interface/common/jaxrs/src/main/java/org/ovirt/engine/api/common/security/auth/Challenger.java
+++ 
b/backend/manager/modules/restapi/interface/common/jaxrs/src/main/java/org/ovirt/engine/api/common/security/auth/Challenger.java
@@ -34,11 +34,18 @@
 import org.jboss.resteasy.spi.interception.PreProcessInterceptor;
 
 import org.ovirt.engine.api.common.invocation.Current;
+import org.ovirt.engine.core.utils.log.Log;
+import org.ovirt.engine.core.utils.log.LogFactory;
 
 @Provider
 @ServerInterceptor
 @Precedence("SECURITY")
 public class Challenger implements PreProcessInterceptor {
+
+    private static final int SECONDS_IN_MINUTE = 60;
+    protected static final Log LOG = LogFactory.getLog(Challenger.class);
+    private static final String SESSION_TTL_EXTRACT_ERROR =
+            "%1$s header content extraction has failed because of bad number 
format: %2$s";
 
     private String realm;
     private Scheme scheme;
@@ -83,11 +90,20 @@
         HttpHeaders headers = request.getHttpHeaders();
         boolean preferPersistentAuth = checkPersistentAuthentication(headers);
         boolean hasAuthorizationHeader = checkAuthorizationHeader(headers);
+        Integer customHttpSessionTtl = getCustomHttpSessionTtl(headers);
 
         // Will create a new one if it is the first session, and we want to 
persist sessions
         // (and then the "isNew" test below will return true)
         // Otherwise, it will return null
         httpSession = getCurrentSession(preferPersistentAuth);
+
+        // Specifies the time, between client requests before the servlet
+        // container will invalidate this session. An interval value of zero
+        // or less indicates that the session should never timeout.
+        if (httpSession != null && customHttpSessionTtl != null) {
+            httpSession.setMaxInactiveInterval(
+                    customHttpSessionTtl.intValue() * SECONDS_IN_MINUTE);
+        }
 
         // If the session isn't new and doesn't carry authorization header, we 
validate it
         if (validator != null && httpSession != null && !httpSession.isNew() 
&& !hasAuthorizationHeader) {
@@ -113,6 +129,35 @@
         return response;
     }
 
+    /**
+     * Extracts the SESSION_TTL_HEADER
+     *
+     * @param headers
+     *            HTTP headers
+     *
+     * @return SESSION_TTL_HEADER or null
+     */
+    private Integer getCustomHttpSessionTtl(HttpHeaders headers) {
+        Integer ttl = null;
+
+        List<String> sessionTtlFields = SessionUtils.getHeaderField(
+                headers,
+                SessionUtils.SESSION_TTL_HEADER_FIELD);
+
+        if (sessionTtlFields != null && !sessionTtlFields.isEmpty()) {
+            try {
+                return Integer.valueOf(sessionTtlFields.get(0));
+            } catch (NumberFormatException e) {
+                LOG.error(String.format(
+                        SESSION_TTL_EXTRACT_ERROR,
+                        SessionUtils.SESSION_TTL_HEADER_FIELD,
+                        sessionTtlFields.get(0)));
+            }
+        }
+
+        return ttl;
+    }
+
     /*
      * This method executes the basic authentication, and returns true whether 
it was successful and false otherwise.
      * It also sets the logged-in principal and the challenger object in the 
Current object
diff --git 
a/backend/manager/modules/restapi/interface/common/jaxrs/src/main/java/org/ovirt/engine/api/common/security/auth/SessionUtils.java
 
b/backend/manager/modules/restapi/interface/common/jaxrs/src/main/java/org/ovirt/engine/api/common/security/auth/SessionUtils.java
index ccddaae..8000c0c 100644
--- 
a/backend/manager/modules/restapi/interface/common/jaxrs/src/main/java/org/ovirt/engine/api/common/security/auth/SessionUtils.java
+++ 
b/backend/manager/modules/restapi/interface/common/jaxrs/src/main/java/org/ovirt/engine/api/common/security/auth/SessionUtils.java
@@ -17,6 +17,7 @@
 public class SessionUtils {
     public static String ENGINE_SESSION_ID_KEY = "engineSessionId";
     public static String PREFER_HEADER_FIELD = "Prefer";
+    public static String SESSION_TTL_HEADER_FIELD = "Session-TTL";
     public static String PERSIST_FIELD_VALUE = "persistent-auth";
     public static String JSESSIONID_HEADER = "JSESSIONID";
     private static final Log log = LogFactory.getLog(SessionUtils.class);
diff --git 
a/backend/manager/modules/restapi/interface/common/jaxrs/src/test/java/org/ovirt/engine/api/common/security/auth/ChallengerTest.java
 
b/backend/manager/modules/restapi/interface/common/jaxrs/src/test/java/org/ovirt/engine/api/common/security/auth/ChallengerTest.java
index e9e4997..c322a7b 100644
--- 
a/backend/manager/modules/restapi/interface/common/jaxrs/src/test/java/org/ovirt/engine/api/common/security/auth/ChallengerTest.java
+++ 
b/backend/manager/modules/restapi/interface/common/jaxrs/src/test/java/org/ovirt/engine/api/common/security/auth/ChallengerTest.java
@@ -118,6 +118,32 @@
     }
 
     @Test
+    public void testValidSessionTtl() {
+        HttpSession httpSession = new TestHttpSession(sessionId, false);
+        doReturn(httpSession).when(challenger).getCurrentSession(anyBoolean());
+        challenger.setValidator(new ConstValidator(true, sessionId));
+        ResourceMethod resource = control.createMock(ResourceMethod.class);
+        ServerResponse response =
+                challenger.preProcess(setUpRequestExpectations(
+                        null, true, true, String.valueOf(5)), resource);
+        assertNull(response);
+        control.verify();
+    }
+
+    @Test
+    public void testNotValidSessionTtl() {
+        HttpSession httpSession = new TestHttpSession(sessionId, false);
+        doReturn(httpSession).when(challenger).getCurrentSession(anyBoolean());
+        challenger.setValidator(new ConstValidator(true, sessionId));
+        ResourceMethod resource = control.createMock(ResourceMethod.class);
+        ServerResponse response =
+                challenger.preProcess(setUpRequestExpectations(
+                        null, true, true, "bad-ttl"), resource);
+        assertNull(response);
+        control.verify();
+    }
+
+    @Test
     public void testValidateSessionFalseOnWrongEngineSessionId() {
         HttpSession httpSession = new TestHttpSession(sessionId, false);
         doReturn(httpSession).when(challenger).getCurrentSession(anyBoolean());
@@ -156,6 +182,10 @@
     }
 
     private HttpRequest setUpRequestExpectations(String credentials, boolean 
valid, boolean preferHeader) {
+        return setUpRequestExpectations(credentials, valid, preferHeader, 
null);
+    }
+
+    private HttpRequest setUpRequestExpectations(String credentials, boolean 
valid, boolean preferHeader, String ttl) {
         Scheme authorizer = control.createMock(Scheme.class);
         challenger.setScheme(authorizer);
         Current current = control.createMock(Current.class);
@@ -181,6 +211,11 @@
                 EasyMock.expectLastCall();
             }
         }
+        if (ttl != null) {
+            List<String> preferHeaders = new ArrayList<String>();
+            preferHeaders.add(ttl);
+            
expect(headers.getRequestHeader(SessionUtils.SESSION_TTL_HEADER_FIELD)).andReturn(preferHeaders);
+        }
         control.replay();
         return request;
     }
diff --git 
a/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd
 
b/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd
index 16578c9..0bcd249 100644
--- 
a/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd
+++ 
b/backend/manager/modules/restapi/interface/definition/src/main/resources/api.xsd
@@ -2831,6 +2831,20 @@
       </xs:extension>
     </xs:complexContent>
   </xs:complexType>
+  
+  
+  <xs:element name="entry_point" type="EntryPoint"/>
+
+  <xs:complexType name="EntryPoint">
+    <xs:complexContent>
+      <xs:extension base="DetailedLink">
+        <xs:sequence>
+          <xs:element name="name" type="xs:string" minOccurs="0" 
maxOccurs="1"/>
+          <xs:element name="description" type="xs:string" minOccurs="0" 
maxOccurs="1"/>
+        </xs:sequence>
+      </xs:extension>
+    </xs:complexContent>
+  </xs:complexType>
 
   <xs:element name="rsdl" type="RSDL"/>
 
@@ -2839,6 +2853,7 @@
        <xs:element name="description" type="xs:string" minOccurs="0"/>
        <xs:element type="Version" name="version" minOccurs="0" maxOccurs="1" />
        <xs:element ref="schema" minOccurs="0" maxOccurs="1" />
+       <xs:element type="EntryPoint" name="api" minOccurs="0"/>      
        <xs:element type="DetailedLinks" name="links" minOccurs="0"/>
      </xs:sequence>
      <xs:attribute name="href" type="xs:string"/>
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendApiResource.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendApiResource.java
index 8fa6182..646fc10 100644
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendApiResource.java
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/resource/BackendApiResource.java
@@ -53,6 +53,7 @@
 import org.ovirt.engine.api.model.Users;
 import org.ovirt.engine.api.model.VMs;
 import org.ovirt.engine.api.resource.ApiResource;
+import org.ovirt.engine.api.restapi.rsdl.EntryPointBuilder;
 import org.ovirt.engine.api.restapi.rsdl.RsdlBuilder;
 import org.ovirt.engine.api.restapi.rsdl.SchemaBuilder;
 import org.ovirt.engine.api.restapi.types.DateMapper;
@@ -81,9 +82,13 @@
     private static final String RSDL_DESCRIPTION = "The oVirt RESTful API 
description language.";
     private static final String SCHEMA_DESCRIPTION = "oVirt API entities 
schema.";
     private static final String SCHEMA_NAME = "ovirt-engine-api-schema.xsd";
+    private static final String QUERY_PARAMETER = "?";
+    private static final String ENTRY_POINT_REL = "api";
+    private static final String ENTRY_POINT_NAME = "root";
+    private static final String ENTRY_POINT_DESCRIPTION = "The oVirt RESTful 
API.";
 
     private static RSDL rsdl = null;
-    private static final String QUERY_PARAMETER = "?";
+
     protected final ObjectFactory OBJECT_FACTORY = new ObjectFactory();
     ApplicationMode appMode = ApplicationMode.AllModes;
 
@@ -351,15 +356,24 @@
 
     private synchronized RSDL getRSDL() {
         if (rsdl == null) {
-            rsdl =  new RsdlBuilder(this).description(RSDL_DESCRIPTION).
-                                          rel(RSDL_REL).
-                                          
href(getUriInfo().getBaseUri().getPath() + QUERY_PARAMETER + 
RSDL_CONSTRAINT_PARAMETER).
-                                          schema(new 
SchemaBuilder().rel(SCHEMA_REL)
-                                                                    
.href(getUriInfo().getBaseUri().getPath() + QUERY_PARAMETER + 
SCHEMA_CONSTRAINT_PARAMETER)
-                                                                    
.name(SCHEMA_NAME)
-                                                                    
.description(SCHEMA_DESCRIPTION)
-                                                                    .build()).
-                                          build();
+            rsdl = new RsdlBuilder(this).description(RSDL_DESCRIPTION)
+                    .rel(RSDL_REL)
+                    .href(getUriInfo().getBaseUri().getPath() +
+                            QUERY_PARAMETER + RSDL_CONSTRAINT_PARAMETER)
+                    .schema(new SchemaBuilder()
+                            .rel(SCHEMA_REL)
+                            .href(getUriInfo().getBaseUri().getPath() +
+                                    QUERY_PARAMETER + 
SCHEMA_CONSTRAINT_PARAMETER)
+                            .name(SCHEMA_NAME)
+                            .description(SCHEMA_DESCRIPTION)
+                            .build())
+                    .entryPoint(new EntryPointBuilder()
+                            .rel(ENTRY_POINT_REL)
+                            .href(getUriInfo().getBaseUri().getPath())
+                            .name(ENTRY_POINT_NAME)
+                            .description(ENTRY_POINT_DESCRIPTION)
+                            .build())
+                    .build();
         }
         return rsdl;
     }
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/rsdl/EntryPointBuilder.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/rsdl/EntryPointBuilder.java
new file mode 100644
index 0000000..ac3819c
--- /dev/null
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/rsdl/EntryPointBuilder.java
@@ -0,0 +1,81 @@
+/*
+* Copyright (c) 2010 Red Hat, Inc.
+*
+* Licensed 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.ovirt.engine.api.restapi.rsdl;
+
+import org.ovirt.engine.api.common.security.auth.SessionUtils;
+import org.ovirt.engine.api.model.EntryPoint;
+import org.ovirt.engine.api.model.Header;
+import org.ovirt.engine.api.model.Headers;
+import org.ovirt.engine.api.model.Request;
+
+public class EntryPointBuilder {
+
+    private EntryPoint entryPoint;
+
+    public EntryPointBuilder() {
+        this.entryPoint = new EntryPoint();
+    }
+
+    public EntryPoint build() {
+        produceRequestHeaders();
+
+        return this.entryPoint;
+    }
+
+    private void produceRequestHeaders() {
+        this.entryPoint.setRequest(new Request());
+        this.entryPoint.getRequest().setHeaders(new Headers());
+
+        injectSessionTtlHeader(this.entryPoint.getRequest().getHeaders());
+    }
+
+    private void injectSessionTtlHeader(Headers headers) {
+        String DESCRIPTION =
+                "Idle session TTL. An interval value of zero\n" +
+                        "or less indicates that the session should never 
timeout";
+
+        if (headers != null) {
+            Header header = new Header();
+            header.setRequired(false);
+            header.setName(SessionUtils.SESSION_TTL_HEADER_FIELD);
+            header.setValue("minutes");
+            header.setDescription(DESCRIPTION);
+
+            headers.getHeaders().add(header);
+        }
+    }
+
+    public EntryPointBuilder description(String description) {
+        this.entryPoint.setDescription(description);
+        return this;
+    }
+
+    public EntryPointBuilder href(String href) {
+        this.entryPoint.setHref(href);
+        return this;
+    }
+
+    public EntryPointBuilder name(String name) {
+        this.entryPoint.setName(name);
+        return this;
+    }
+
+    public EntryPointBuilder rel(String rel) {
+        this.entryPoint.setRel(rel);
+        return this;
+    }
+}
diff --git 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/rsdl/RsdlBuilder.java
 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/rsdl/RsdlBuilder.java
index 148e417..a3cbb79 100644
--- 
a/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/rsdl/RsdlBuilder.java
+++ 
b/backend/manager/modules/restapi/jaxrs/src/main/java/org/ovirt/engine/api/restapi/rsdl/RsdlBuilder.java
@@ -43,6 +43,7 @@
 import org.ovirt.engine.api.model.Body;
 import org.ovirt.engine.api.model.DetailedLink;
 import org.ovirt.engine.api.model.DetailedLinks;
+import org.ovirt.engine.api.model.EntryPoint;
 import org.ovirt.engine.api.model.Header;
 import org.ovirt.engine.api.model.Headers;
 import org.ovirt.engine.api.model.HttpMethod;
@@ -67,12 +68,13 @@
     private static final String COLLECTION_PARAMETER_RSDL = "collection";
     private static final String COLLECTION_PARAMETER_YAML = "--COLLECTION";
     private RSDL rsdl;
-    private String entryPoint;
+    private String entryPointPath;
     private BackendApiResource apiResource;
     private Map<String, Action> parametersMetaData;
     private String rel;
     private String href;
     private Schema schema;
+    private EntryPoint entryPoint;
     private String description;
 
     private static final String ACTION = "Action";
@@ -88,7 +90,7 @@
 
     public RsdlBuilder(BackendApiResource apiResource) {
         this.apiResource = apiResource;
-        this.entryPoint = apiResource.getUriInfo().getBaseUri().getPath();
+        this.entryPointPath = apiResource.getUriInfo().getBaseUri().getPath();
         this.parametersMetaData = loadParametersMetaData();
     }
 
@@ -138,6 +140,7 @@
             rsdl.setHref(getHref());
             rsdl.setDescription(getDescription());
             rsdl.setSchema(getSchema());
+            rsdl.setApi(getEntryPoint());
         } catch (Exception e) {
             e.printStackTrace();
             LOG.error("RSDL generation failure.", e);
@@ -160,6 +163,11 @@
         return this;
     }
 
+    public RsdlBuilder entryPoint(EntryPoint entryPoint) {
+        this.entryPoint = entryPoint;
+        return this;
+    }
+
     public RsdlBuilder description(String description) {
         this.description = description;
         return this;
@@ -175,6 +183,10 @@
 
     public Schema getSchema() {
         return schema;
+    }
+
+    public EntryPoint getEntryPoint() {
+        return entryPoint;
     }
 
     public String getDescription() {
@@ -228,7 +240,7 @@
         List<Class<?>> classes = 
ReflectionHelper.getClasses(RESOURCES_PACKAGE);
         for (String path : apiResource.getRels()) {
             Class<?> resource = findResource(path, classes);
-            String prefix = entryPoint.endsWith("/") ? entryPoint + path : 
entryPoint + "/" + path;
+            String prefix = entryPointPath.endsWith("/") ? entryPointPath + 
path : entryPointPath + "/" + path;
             results.addAll(describe(resource, prefix, new HashMap<String, 
Type>()));
         }
         return results;
diff --git 
a/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml 
b/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml
index 9f60e58..eade16d 100644
--- a/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml
+++ b/backend/manager/modules/restapi/webapp/src/main/webapp/WEB-INF/web.xml
@@ -23,4 +23,7 @@
     </user-data-constraint>
   </security-constraint>
 
+  <session-config>
+    <session-timeout>180</session-timeout>
+  </session-config>
 </web-app>


--
To view, visit http://gerrit.ovirt.org/13044
To unsubscribe, visit http://gerrit.ovirt.org/settings

Gerrit-MessageType: newchange
Gerrit-Change-Id: I753a379c60b2d7f88867cd0123ee69321a331f0f
Gerrit-PatchSet: 1
Gerrit-Project: ovirt-engine
Gerrit-Branch: master
Gerrit-Owner: Michael Pasternak <mpast...@redhat.com>
_______________________________________________
Engine-patches mailing list
Engine-patches@ovirt.org
http://lists.ovirt.org/mailman/listinfo/engine-patches

Reply via email to