View Javadoc
1   /*
2    * Copyright 2013–2026 Michael Osipov
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, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  package net.sf.michaelo.tomcat.realm;
17  
18  import java.security.Principal;
19  import java.util.Arrays;
20  import java.util.Collections;
21  import java.util.Enumeration;
22  import java.util.List;
23  import java.util.Map;
24  
25  import org.apache.catalina.TomcatPrincipal;
26  import org.ietf.jgss.GSSCredential;
27  import org.ietf.jgss.GSSName;
28  
29  /**
30   * Represents a principal from Active Directory with a list of roles.
31   *
32   * <p>An Active Directory principal is comprised of the following items:
33   *
34   * <ul>
35   *   <li>the GSS name,
36   *   <li>the security identifier (SID),
37   *   <li>an optional GSS credential for credential delegation (impersonation),
38   *   <li>an array of security groups the user has been assigned to, stored according to the role format configured in
39   *       the realm,
40   *   <li>and a map with additional attributes which are either a {@code String}, {@code byte[]} or a {@code List} of
41   *       either one.
42   * </ul>
43   */
44  public class ActiveDirectoryPrincipal implements TomcatPrincipal {
45  
46  	private final GSSName gssName;
47  	private final Sid sid;
48  	private final transient GSSCredential gssCredential;
49  	private final String[] roles;
50  	private final Map<String, Object> additionalAttributes;
51  
52  	/** Constructs a new principal for the given parameters. */
53  	public ActiveDirectoryPrincipal(GSSName gssName, Sid sid, GSSCredential gssCredential) {
54  		this(gssName, sid, null, gssCredential, null);
55  	}
56  
57  	/** Constructs a new principal for the given parameters. */
58  	public ActiveDirectoryPrincipal(
59  			GSSName gssName,
60  			Sid sid,
61  			List<String> roles,
62  			GSSCredential gssCredential,
63  			Map<String, Object> additionalAttributes) {
64  		this.gssName = gssName;
65  		this.sid = sid;
66  		if (roles == null || roles.isEmpty()) this.roles = new String[0];
67  		else {
68  			this.roles = roles.toArray(new String[0]);
69  			Arrays.sort(this.roles);
70  		}
71  		this.gssCredential = gssCredential;
72  		if (additionalAttributes == null || additionalAttributes.isEmpty())
73  			this.additionalAttributes = Collections.emptyMap();
74  		else this.additionalAttributes = Collections.unmodifiableMap(additionalAttributes);
75  	}
76  
77  	@Override
78  	public Principal getUserPrincipal() {
79  		return this;
80  	}
81  
82  	@Override
83  	public String getName() {
84  		return gssName.toString();
85  	}
86  
87  	/**
88  	 * Returns the underlying GSS name.
89  	 *
90  	 * @return the underlying GSS name
91  	 */
92  	public GSSName getGssName() {
93  		return gssName;
94  	}
95  
96  	/**
97  	 * Returns the security identifier (SID) of the principal.
98  	 *
99  	 * @return the security identifier
100 	 */
101 	public Sid getSid() {
102 		return sid;
103 	}
104 
105 	@Override
106 	public GSSCredential getGssCredential() {
107 		return gssCredential;
108 	}
109 
110 	/**
111 	 * Grants access if supplied role is associated with this principal.
112 	 *
113 	 * @param role the role to check
114 	 * @return true if principal is associated with the role, else false
115 	 */
116 	public boolean hasRole(String role) {
117 		if ("*".equals(role)) // Special 2.4 role meaning everyone
118 		return true;
119 		if (role == null) return false;
120 		return Arrays.binarySearch(roles, role) >= 0;
121 	}
122 
123 	/**
124 	 * Returns the sorted roles of the given principal.
125 	 *
126 	 * @return a sorted read-only view of the roles
127 	 */
128 	public String[] getRoles() {
129 		return Arrays.copyOf(roles, roles.length);
130 	}
131 
132 	@Override
133 	public Object getAttribute(String name) {
134 		return additionalAttributes.get(name);
135 	}
136 
137 	@Override
138 	public Enumeration<String> getAttributeNames() {
139 		return Collections.enumeration(additionalAttributes.keySet());
140 	}
141 
142 	/**
143 	 * Holds additional attributes for a given principal which may be stored in Active Directory.
144 	 *
145 	 * @return a read-only view of the additional attributes
146 	 */
147 	public Map<String, Object> getAdditionalAttributes() {
148 		return additionalAttributes;
149 	}
150 
151 	@Override
152 	public boolean equals(Object obj) {
153 		if (obj == null) return false;
154 
155 		if (!(obj instanceof ActiveDirectoryPrincipal)) return false;
156 
157 		ActiveDirectoryPrincipal other = (ActiveDirectoryPrincipal) obj;
158 
159 		return gssName.equals((Object) other.gssName);
160 	}
161 
162 	@Override
163 	public int hashCode() {
164 		return gssName.hashCode();
165 	}
166 
167 	@Override
168 	public String toString() {
169 		return gssName.toString();
170 	}
171 
172 	@Override
173 	public void logout() throws Exception {
174 		if (gssCredential != null) {
175 			gssCredential.dispose();
176 		}
177 	}
178 }