001/* 002 * Licensed to DuraSpace under one or more contributor license agreements. 003 * See the NOTICE file distributed with this work for additional information 004 * regarding copyright ownership. 005 * 006 * DuraSpace licenses this file to you under the Apache License, 007 * Version 2.0 (the "License"); you may not use this file except in 008 * compliance with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.fcrepo.http.commons.session; 019 020import static java.util.Objects.requireNonNull; 021import static org.slf4j.LoggerFactory.getLogger; 022 023import java.security.Principal; 024 025import javax.annotation.PostConstruct; 026import javax.inject.Inject; 027import javax.servlet.http.HttpServletRequest; 028 029import org.fcrepo.kernel.api.FedoraRepository; 030import org.fcrepo.kernel.api.FedoraSession; 031import org.fcrepo.kernel.api.exception.SessionMissingException; 032import org.fcrepo.kernel.api.services.CredentialsService; 033import org.fcrepo.kernel.api.services.BatchService; 034 035import org.slf4j.Logger; 036 037/** 038 * Factory for generating sessions for HTTP requests, taking 039 * into account transactions and authentication. 040 * 041 * @author awoods 042 * @author gregjan 043 * @author kaisternad 044 */ 045public class SessionFactory { 046 047 protected enum Prefix{ 048 TX("tx:"); 049 050 private final String prefix; 051 052 Prefix(final String prefix) { 053 this.prefix = prefix; 054 } 055 056 public String getPrefix() { 057 return prefix; 058 } 059 } 060 061 private static final Logger LOGGER = getLogger(SessionFactory.class); 062 063 @Inject 064 private FedoraRepository repo; 065 066 @Inject 067 private BatchService batchService; 068 069 @Inject 070 private CredentialsService credentialsService; 071 072 /** 073 * Initialize a session factory for the given Repository 074 * 075 * @param repo the repository 076 * @param batchService the transaction service 077 */ 078 public SessionFactory(final FedoraRepository repo, final BatchService batchService) { 079 this.repo = repo; 080 this.batchService = batchService; 081 } 082 083 /** 084 * Validate the spring wiring 085 */ 086 @PostConstruct 087 public void init() { 088 requireNonNull(repo, "SessionFactory requires a Repository instance!"); 089 } 090 091 /** 092 * Get a new JCR Session 093 * 094 * @return an internal session 095 */ 096 public FedoraSession getInternalSession() { 097 return repo.login(); 098 } 099 100 /** 101 * Get a JCR session for the given HTTP servlet request with a 102 * SecurityContext attached 103 * 104 * @param servletRequest the servlet request 105 * @return the Session 106 * @throws RuntimeException if the transaction could not be found 107 */ 108 public HttpSession getSession(final HttpServletRequest servletRequest) { 109 final HttpSession session; 110 final String txId = getEmbeddedId(servletRequest, Prefix.TX); 111 112 try { 113 if (txId == null) { 114 session = createSession(servletRequest); 115 } else { 116 session = getSessionFromTransaction(servletRequest, txId); 117 } 118 } catch (final SessionMissingException e) { 119 LOGGER.warn("Transaction missing: {}", e.getMessage()); 120 return null; 121 } 122 123 return session; 124 } 125 126 /** 127 * Create a JCR session for the given HTTP servlet request with a 128 * SecurityContext attached. 129 * 130 * @param servletRequest the servlet request 131 * @return a newly created JCR session 132 */ 133 protected HttpSession createSession(final HttpServletRequest servletRequest) { 134 135 LOGGER.debug("Returning an authenticated session in the default workspace"); 136 return new HttpSession(repo.login(credentialsService.getCredentials(servletRequest))); 137 } 138 139 /** 140 * Retrieve a JCR session from an active transaction 141 * 142 * @param servletRequest the servlet request 143 * @param txId the transaction id 144 * @return a JCR session that is associated with the transaction 145 */ 146 protected HttpSession getSessionFromTransaction(final HttpServletRequest servletRequest, final String txId) { 147 148 final Principal userPrincipal = servletRequest.getUserPrincipal(); 149 final String userName = userPrincipal == null ? null : userPrincipal.getName(); 150 151 final FedoraSession session = batchService.getSession(txId, userName); 152 final HttpSession batchSession = new HttpSession(session); 153 batchSession.makeBatchSession(); 154 LOGGER.debug("Returning a session in the batch {} for user {}", batchSession, userName); 155 return batchSession; 156 } 157 158 /** 159 * Extract the id embedded at the beginning of a request path 160 * 161 * @param servletRequest the servlet request 162 * @param prefix the prefix for the id 163 * @return the found id or null 164 */ 165 protected String getEmbeddedId( 166 final HttpServletRequest servletRequest, final Prefix prefix) { 167 String requestPath = servletRequest.getPathInfo(); 168 169 // http://stackoverflow.com/questions/18963562/grizzlys-request-getpathinfo-returns-always-null 170 if (requestPath == null && servletRequest.getContextPath().isEmpty()) { 171 requestPath = servletRequest.getRequestURI(); 172 } 173 174 String id = null; 175 if (requestPath != null) { 176 final String pathPrefix = prefix.getPrefix(); 177 final String[] part = requestPath.split("/"); 178 if (part.length > 1 && part[1].startsWith(pathPrefix)) { 179 id = part[1].substring(pathPrefix.length()); 180 } 181 } 182 return id; 183 } 184 185}