View Javadoc
1   /*
2    * Copyright 2012-2025 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.app.web.base.login;
17  
18  import static org.codelibs.core.stream.StreamUtil.split;
19  import static org.codelibs.core.stream.StreamUtil.stream;
20  
21  import java.util.ArrayList;
22  import java.util.Arrays;
23  import java.util.HashSet;
24  import java.util.List;
25  import java.util.Map;
26  import java.util.Set;
27  
28  import org.codelibs.core.lang.StringUtil;
29  import org.codelibs.fess.entity.FessUser;
30  import org.codelibs.fess.helper.SystemHelper;
31  import org.codelibs.fess.mylasta.direction.FessConfig;
32  import org.codelibs.fess.util.ComponentUtil;
33  import org.codelibs.saml2.Auth;
34  import org.lastaflute.web.login.credential.LoginCredential;
35  
36  /**
37   * Credential for SAML authentication.
38   */
39  public class SamlCredential implements LoginCredential, FessCredential {
40  
41      private final Map<String, List<String>> attributes;
42  
43      private final String nameId;
44  
45      private final String nameIdFormat;
46  
47      private final String sessionIndex;
48  
49      private final String nameidNameQualifier;
50  
51      private final String nameidSPNameQualifier;
52  
53      /**
54       * Constructor.
55       * @param auth The SAML authentication.
56       */
57      public SamlCredential(final Auth auth) {
58          attributes = auth.getAttributes();
59          nameId = auth.getNameId();
60          nameIdFormat = auth.getNameIdFormat();
61          sessionIndex = auth.getSessionIndex();
62          nameidNameQualifier = auth.getNameIdNameQualifier();
63          nameidSPNameQualifier = auth.getNameIdSPNameQualifier();
64      }
65  
66      @Override
67      public String toString() {
68          return "{" + getUserId() + "}";
69      }
70  
71      @Override
72      public String getUserId() {
73          return nameId;
74      }
75  
76      /**
77       * Gets the SAML user.
78       * @return The SAML user.
79       */
80      public SamlUser getUser() {
81          return new SamlUser(nameId, sessionIndex, nameIdFormat, nameidNameQualifier, nameidSPNameQualifier, getDefaultGroupsAsArray(),
82                  getDefaultRolesAsArray());
83      }
84  
85      /**
86       * Gets the default groups as an array.
87       * @return The default groups as an array.
88       */
89      protected String[] getDefaultGroupsAsArray() {
90          final List<String> list = new ArrayList<>();
91          final FessConfig fessConfig = ComponentUtil.getFessConfig();
92          final String key = fessConfig.getSystemProperty("saml.attribute.group.name", "memberOf");
93          if (StringUtil.isNotBlank(key)) {
94              final List<String> nameList = attributes.get(key);
95              if (nameList != null) {
96                  list.addAll(nameList);
97              }
98          }
99          final String value = fessConfig.getSystemProperty("saml.default.groups");
100         if (StringUtil.isNotBlank(value)) {
101             split(value, ",").of(stream -> stream.forEach(list::add));
102         }
103         return list.stream().filter(StringUtil::isNotBlank).map(String::trim).toArray(n -> new String[n]);
104     }
105 
106     /**
107      * Gets the default roles as an array.
108      * @return The default roles as an array.
109      */
110     protected String[] getDefaultRolesAsArray() {
111         final List<String> list = new ArrayList<>();
112         final FessConfig fessConfig = ComponentUtil.getFessConfig();
113         final String key = fessConfig.getSystemProperty("saml.attribute.role.name");
114         if (StringUtil.isNotBlank(key)) {
115             final List<String> nameList = attributes.get(key);
116             if (nameList != null) {
117                 list.addAll(nameList);
118             }
119         }
120         final String value = fessConfig.getSystemProperty("saml.default.roles");
121         if (StringUtil.isNotBlank(value)) {
122             split(value, ",").of(stream -> stream.forEach(list::add));
123         }
124         return list.stream().filter(StringUtil::isNotBlank).map(String::trim).toArray(n -> new String[n]);
125     }
126 
127     /**
128      * Represents a SAML user.
129      */
130     public static class SamlUser implements FessUser {
131 
132         private static final long serialVersionUID = 1L;
133 
134         /**
135          * The groups of the user.
136          */
137         protected String[] groups;
138 
139         /**
140          * The roles of the user.
141          */
142         protected String[] roles;
143 
144         /**
145          * The permissions of the user.
146          */
147         protected String[] permissions;
148 
149         /**
150          * The name ID of the user.
151          */
152         protected String nameId;
153 
154         /**
155          * The session index of the user.
156          */
157         protected String sessionIndex;
158 
159         /**
160          * The name ID format of the user.
161          */
162         protected String nameIdFormat;
163 
164         /**
165          * The name ID name qualifier of the user.
166          */
167         protected String nameidNameQualifier;
168 
169         /**
170          * The name ID SP name qualifier of the user.
171          */
172         protected String nameidSPNameQualifier;
173 
174         /**
175          * Constructor.
176          * @param nameId The name ID.
177          * @param sessionIndex The session index.
178          * @param nameIdFormat The name ID format.
179          * @param nameidNameQualifier The name ID name qualifier.
180          * @param nameidSPNameQualifier The name ID SP name qualifier.
181          * @param groups The groups.
182          * @param roles The roles.
183          */
184         public SamlUser(final String nameId, final String sessionIndex, final String nameIdFormat, final String nameidNameQualifier,
185                 final String nameidSPNameQualifier, final String[] groups, final String[] roles) {
186             this.nameId = nameId;
187             this.sessionIndex = sessionIndex;
188             this.nameIdFormat = nameIdFormat;
189             this.nameidNameQualifier = nameidNameQualifier;
190             this.nameidSPNameQualifier = nameidSPNameQualifier;
191             this.groups = groups;
192             this.roles = roles;
193         }
194 
195         @Override
196         public String getName() {
197             return nameId;
198         }
199 
200         @Override
201         public String[] getRoleNames() {
202             return roles;
203         }
204 
205         @Override
206         public String[] getGroupNames() {
207             return groups;
208         }
209 
210         @Override
211         public String[] getPermissions() {
212             if (permissions == null) {
213                 final SystemHelper systemHelper = ComponentUtil.getSystemHelper();
214                 final Set<String> permissionSet = new HashSet<>();
215                 permissionSet.add(systemHelper.getSearchRoleByUser(nameId));
216                 stream(groups).of(stream -> stream.forEach(s -> permissionSet.add(systemHelper.getSearchRoleByGroup(s))));
217                 stream(roles).of(stream -> stream.forEach(s -> permissionSet.add(systemHelper.getSearchRoleByRole(s))));
218                 permissions = permissionSet.toArray(new String[permissionSet.size()]);
219             }
220             return permissions;
221         }
222 
223         /**
224          * Gets the session index.
225          * @return The session index.
226          */
227         public String getSessionIndex() {
228             return sessionIndex;
229         }
230 
231         /**
232          * Gets the name ID format.
233          * @return The name ID format.
234          */
235         public String getNameIdFormat() {
236             return nameIdFormat;
237         }
238 
239         /**
240          * Gets the name ID name qualifier.
241          * @return The name ID name qualifier.
242          */
243         public String getNameidNameQualifier() {
244             return nameidNameQualifier;
245         }
246 
247         /**
248          * Gets the name ID SP name qualifier.
249          * @return The name ID SP name qualifier.
250          */
251         public String getNameidSPNameQualifier() {
252             return nameidSPNameQualifier;
253         }
254 
255         @Override
256         public String toString() {
257             return "SamlUser [groups=" + Arrays.toString(groups) + ", roles=" + Arrays.toString(roles) + ", permissions="
258                     + Arrays.toString(permissions) + ", nameId=" + nameId + ", sessionIndex=" + sessionIndex + ", nameIdFormat="
259                     + nameIdFormat + ", nameidNameQualifier=" + nameidNameQualifier + ", nameidSPNameQualifier=" + nameidSPNameQualifier
260                     + "]";
261         }
262 
263     }
264 }