View Javadoc
1   /*
2    * Copyright 2012-2021 CodeLibs Project and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.codelibs.fess.helper;
17  
18  import static org.codelibs.core.stream.StreamUtil.stream;
19  
20  import java.util.ArrayList;
21  import java.util.HashMap;
22  import java.util.HashSet;
23  import java.util.List;
24  import java.util.Map;
25  import java.util.Set;
26  
27  import javax.annotation.PostConstruct;
28  import javax.servlet.http.Cookie;
29  import javax.servlet.http.HttpServletRequest;
30  
31  import org.apache.logging.log4j.LogManager;
32  import org.apache.logging.log4j.Logger;
33  import org.codelibs.core.crypto.CachedCipher;
34  import org.codelibs.core.lang.StringUtil;
35  import org.codelibs.fess.app.service.AccessTokenService;
36  import org.codelibs.fess.entity.SearchRequestParams.SearchRequestType;
37  import org.codelibs.fess.exception.InvalidAccessTokenException;
38  import org.codelibs.fess.mylasta.action.FessUserBean;
39  import org.codelibs.fess.mylasta.direction.FessConfig;
40  import org.codelibs.fess.util.ComponentUtil;
41  import org.lastaflute.web.login.LoginManager;
42  import org.lastaflute.web.servlet.request.RequestManager;
43  import org.lastaflute.web.util.LaRequestUtil;
44  
45  /**
46   * This class returns a list of a role from a request parameter,
47   * a request header and a cookie. The format of the default value
48   * is "[\d]+\nrole1,role2,role3", which you can encrypt.
49   *
50   * @author shinsuke
51   *
52   */
53  public class RoleQueryHelper {
54  
55      private static final Logger logger = LogManager.getLogger(RoleQueryHelper.class);
56  
57      protected static final String USER_ROLES = "userRoles";
58  
59      protected CachedCipher cipher;
60  
61      protected String valueSeparator = "\n";
62  
63      protected String roleSeparator = ",";
64  
65      protected String parameterKey;
66  
67      protected boolean encryptedParameterValue = true;
68  
69      protected String headerKey;
70  
71      protected boolean encryptedHeaderValue = true;
72  
73      protected String cookieKey;
74  
75      protected boolean encryptedCookieValue = true;
76  
77      protected long maxAge = 30 * 60; // sec
78  
79      protected Map<String, String> cookieNameMap;
80  
81      protected final List<String> defaultRoleList = new ArrayList<>();
82  
83      @PostConstruct
84      public void init() {
85          if (logger.isDebugEnabled()) {
86              logger.debug("Initialize {}", this.getClass().getSimpleName());
87          }
88          stream(ComponentUtil.getFessConfig().getSearchDefaultPermissionsAsArray()).of(stream -> stream.forEach(name -> {
89              defaultRoleList.add(name);
90          }));
91      }
92  
93      public Set<String> build(final SearchRequestType searchRequestType) {
94          final Set<String> roleSet = new HashSet<>();
95          final HttpServletRequest request = LaRequestUtil.getOptionalRequest().orElse(null);
96          final FessConfig fessConfig = ComponentUtil.getFessConfig();
97          final boolean isApiRequest =
98                  !SearchRequestType.SEARCH.equals(searchRequestType) && !SearchRequestType.ADMIN_SEARCH.equals(searchRequestType);
99  
100         if (request != null) {
101             @SuppressWarnings("unchecked")
102             final Set<String> list = (Set<String>) request.getAttribute(USER_ROLES);
103             if (list != null) {
104                 return list;
105             }
106 
107             // request parameter
108             if (StringUtil.isNotBlank(parameterKey)) {
109                 processParameter(request, roleSet);
110             }
111 
112             // request header
113             if (StringUtil.isNotBlank(headerKey)) {
114                 processHeader(request, roleSet);
115             }
116 
117             // cookie
118             if (StringUtil.isNotBlank(cookieKey)) {
119                 processCookie(request, roleSet);
120             }
121 
122             // cookie mapping
123             if (cookieNameMap != null) {
124                 buildByCookieNameMapping(request, roleSet);
125             }
126 
127             final boolean hasAccessToken = processAccessToken(request, roleSet, isApiRequest);
128 
129             final RequestManager requestManager = ComponentUtil.getRequestManager();
130             try {
131                 requestManager.findUserBean(FessUserBean.class)
132                         .ifPresent(fessUserBean -> stream(fessUserBean.getPermissions()).of(stream -> stream.forEach(roleSet::add)))
133                         .orElse(() -> {
134                             if (isApiRequest && ComponentUtil.getFessConfig().getApiAccessTokenRequiredAsBoolean()) {
135                                 throw new InvalidAccessTokenException("invalid_token", "Access token is requried.");
136                             }
137                             if (!hasAccessToken || roleSet.isEmpty()) {
138                                 roleSet.addAll(fessConfig.getSearchGuestPermissionList());
139                             }
140                         });
141             } catch (final RuntimeException e) {
142                 try {
143                     requestManager.findLoginManager(FessUserBean.class).ifPresent(LoginManager::logout);
144                 } catch (final Exception e1) {
145                     // ignore
146                 }
147                 throw e;
148             }
149         }
150 
151         if (defaultRoleList != null) {
152             roleSet.addAll(defaultRoleList);
153         }
154 
155         if (logger.isDebugEnabled()) {
156             logger.debug("roleSet: {}", roleSet);
157         }
158 
159         if (request != null) {
160             request.setAttribute(USER_ROLES, roleSet);
161         }
162         return roleSet;
163     }
164 
165     protected boolean processAccessToken(final HttpServletRequest request, final Set<String> roleSet, final boolean isApiRequest) {
166         if (isApiRequest) {
167             return ComponentUtil.getComponent(AccessTokenService.class).getPermissions(request).map(p -> {
168                 p.forEach(roleSet::add);
169                 return true;
170             }).orElse(false);
171         }
172         return false;
173     }
174 
175     protected void processParameter(final HttpServletRequest request, final Set<String> roleSet) {
176         final String parameter = request.getParameter(parameterKey);
177         if (logger.isDebugEnabled()) {
178             logger.debug("{}:{}", parameterKey, parameter);
179         }
180         if (StringUtil.isNotEmpty(parameter)) {
181             parseRoleSet(parameter, encryptedParameterValue, roleSet);
182         }
183 
184     }
185 
186     protected void processHeader(final HttpServletRequest request, final Set<String> roleSet) {
187 
188         final String parameter = request.getHeader(headerKey);
189         if (logger.isDebugEnabled()) {
190             logger.debug("{}:{}", headerKey, parameter);
191         }
192         if (StringUtil.isNotEmpty(parameter)) {
193             parseRoleSet(parameter, encryptedHeaderValue, roleSet);
194         }
195 
196     }
197 
198     protected void processCookie(final HttpServletRequest request, final Set<String> roleSet) {
199 
200         final Cookie[] cookies = request.getCookies();
201         if (cookies != null) {
202             for (final Cookie cookie : cookies) {
203                 if (cookieKey.equals(cookie.getName())) {
204                     final String value = cookie.getValue();
205                     if (logger.isDebugEnabled()) {
206                         logger.debug("{}:{}", cookieKey, value);
207                     }
208                     if (StringUtil.isNotEmpty(value)) {
209                         parseRoleSet(value, encryptedCookieValue, roleSet);
210                     }
211                 }
212             }
213         }
214 
215     }
216 
217     protected void buildByCookieNameMapping(final HttpServletRequest request, final Set<String> roleSet) {
218         final Cookie[] cookies = request.getCookies();
219         if (cookies != null) {
220             for (final Cookie cookie : cookies) {
221                 addRoleFromCookieMapping(roleSet, cookie);
222             }
223         }
224 
225     }
226 
227     protected void addRoleFromCookieMapping(final Set<String> roleNameList, final Cookie cookie) {
228         final String roleName = cookieNameMap.get(cookie.getName());
229         if (StringUtil.isNotBlank(roleName)) {
230             roleNameList.add(roleName);
231         }
232     }
233 
234     protected void parseRoleSet(final String value, final boolean encrypted, final Set<String> roleSet) {
235         String rolesStr = value;
236         if (encrypted && cipher != null) {
237             try {
238                 rolesStr = cipher.decryptoText(rolesStr);
239             } catch (final Exception e) {
240                 if (logger.isDebugEnabled()) {
241                     logger.debug("Failed to decrypt {}", rolesStr, e);
242                 }
243                 return;
244             }
245         }
246 
247         if (logger.isDebugEnabled()) {
248             logger.debug("role: original: {}, decrypto: {}", value, rolesStr);
249         }
250 
251         if (valueSeparator.length() > 0) {
252             final String[] values = rolesStr.split(valueSeparator);
253             if (maxAge > 0) {
254                 try {
255                     final long time = getCurrentTime() / 1000 - Long.parseLong(values[0]);
256                     if (time > maxAge || time < 0) {
257                         if (logger.isDebugEnabled()) {
258                             logger.debug("role info is expired: {} > {}", time, maxAge);
259                         }
260                         return;
261                     }
262                 } catch (final NumberFormatException e) {
263                     logger.warn("Invalid role info: {}", rolesStr, e);
264                     return;
265                 }
266             }
267             if (values.length > 1) {
268                 final String[] roles = values[1].split(roleSeparator);
269                 for (final String role : roles) {
270                     if (StringUtil.isNotEmpty(role)) {
271                         roleSet.add(role);
272                     }
273                 }
274             }
275         } else {
276             final String[] roles = rolesStr.split(roleSeparator);
277             for (final String role : roles) {
278                 if (StringUtil.isNotEmpty(role)) {
279                     roleSet.add(role);
280                 }
281             }
282         }
283     }
284 
285     protected long getCurrentTime() {
286         return ComponentUtil.getSystemHelper().getCurrentTimeAsLong();
287     }
288 
289     public void addCookieNameMapping(final String cookieName, final String roleName) {
290         if (cookieNameMap == null) {
291             cookieNameMap = new HashMap<>();
292         }
293         cookieNameMap.put(cookieName, roleName);
294     }
295 
296     public void setCipher(final CachedCipher cipher) {
297         this.cipher = cipher;
298     }
299 
300     public void setValueSeparator(final String valueSeparator) {
301         this.valueSeparator = valueSeparator;
302     }
303 
304     public void setRoleSeparator(final String roleSeparator) {
305         this.roleSeparator = roleSeparator;
306     }
307 
308     public void setParameterKey(final String parameterKey) {
309         this.parameterKey = parameterKey;
310     }
311 
312     public void setEncryptedParameterValue(final boolean encryptedParameterValue) {
313         this.encryptedParameterValue = encryptedParameterValue;
314     }
315 
316     public void setHeaderKey(final String headerKey) {
317         this.headerKey = headerKey;
318     }
319 
320     public void setEncryptedHeaderValue(final boolean encryptedHeaderValue) {
321         this.encryptedHeaderValue = encryptedHeaderValue;
322     }
323 
324     public void setCookieKey(final String cookieKey) {
325         this.cookieKey = cookieKey;
326     }
327 
328     public void setEncryptedCookieValue(final boolean encryptedCookieValue) {
329         this.encryptedCookieValue = encryptedCookieValue;
330     }
331 
332     public void setMaxAge(final long maxAge) {
333         this.maxAge = maxAge;
334     }
335 
336 }