File D:\maven-qcr-plugin-testsunitaires\qcr4java\jenkins-master\target\site\qc\core\src\main\java\hudson\security\FederatedLoginService.java.html

1 /*
2 * The MIT License
3 *
4 * Copyright (c) 2010, CloudBees, Inc.
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22 * THE SOFTWARE.
23 */
24 package hudson.security;
25
26 import hudson.ExtensionList;
27 import hudson.ExtensionPoint;
28 import jenkins.model.Jenkins;
29 import hudson.model.User;
30 import hudson.model.UserProperty;
31 import org.acegisecurity.context.SecurityContextHolder;
32 import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
33 import org.acegisecurity.userdetails.UserDetails;
34 import org.kohsuke.stapler.HttpResponse;
35 import org.kohsuke.stapler.StaplerRequest;
36 import org.kohsuke.stapler.StaplerResponse;
37
38 import javax.servlet.ServletException;
39 import java.io.IOException;
40 import java.io.Serializable;
41
42 /**
43 * Abstraction for a login mechanism through external authenticator/identity provider
44 * (instead of username/password.)
45 *
46 * <p>
47 * This extension point adds additional login mechanism for {@link SecurityRealm}s that
48 * authenticate the user via username/password (which typically extends from {@link AbstractPasswordBasedSecurityRealm}.)
49 * The intended use case is protocols like OpenID, OAuth, and other SSO-like services.
50 *
51 * <p>
52 * The basic abstraction is that:
53 *
54 * <ul>
55 * <li>
56 * The user can have (possibly multiple, possibly zero) opaque strings to their {@linkplain User} object.
57 * Such opaque strings are called "identifiers."
58 * Think of them as OpenID URLs, twitter account names, etc.
59 * Identifiers are only comparable within the same {@link FederatedLoginService} implementation.
60 *
61 * <li>
62 * After getting authenticated by some means, the user can add additional identifiers to their account.
63 * Your implementation would do protocol specific thing to verify that the user indeed owns the claimed identifier,
64 * create a {@link FederatedIdentity} instance,
65 * then call {@link FederatedIdentity#addToCurrentUser()} to record such association.
66 *
67 * <li>
68 * In the login page, instead of entering the username and password, the user opts for authenticating
69 * via other services. Think of OpenID, OAuth, your corporate SSO service, etc.
70 * The user proves (by your protocol specific way) that they own some identifier, then
71 * create a {@link FederatedIdentity} instance, and invoke {@link FederatedIdentity#signin()} to sign in that user.
72 *
73 * </ul>
74 *
75 *
76 * <h2>Views</h2>
77 * <dl>
78 * <dt>loginFragment.jelly
79 * <dd>
80 * Injected into the login form page, after the default "login" button but before
81 * the "create account" link. Use this to generate a button or a link so that the user
82 * can initiate login via your federated login service.
83 * </dl>
84 *
85 * <h2>URL Binding</h2>
86 * <p>
87 * Each {@link FederatedLoginService} is exposed to the URL space via {@link jenkins.model.Jenkins#getFederatedLoginService(String)}.
88 * So for example if your {@linkplain #getUrlName() url name} is "openid", this object gets
89 * "/federatedLoginService/openid" as the URL.
90 *
91 * @author Kohsuke Kawaguchi
92 * @since 1.394
93 */
94 public abstract class FederatedLoginService implements ExtensionPoint {
95 /**
96 * Returns the url name that determines where this {@link FederatedLoginService} is mapped to in the URL space.
97 *
98 * <p>
99 * The object is bound to /federatedLoginService/URLNAME/. The url name needs to be unique among all
100 * {@link FederatedLoginService}s.
101 */
102 public abstract String getUrlName();
103
104 /**
105 * Returns your implementation of {@link FederatedLoginServiceUserProperty} that stores
106 * opaque identifiers.
107 */
108 public abstract Class<? extends FederatedLoginServiceUserProperty> getUserPropertyClass();
109
110 /**
111 * Identity information as obtained from {@link FederatedLoginService}.
112 */
113 public abstract class FederatedIdentity implements Serializable {
114 /**
115 * Gets the string representation of the identity in the form that makes sense to the enclosing
116 * {@link FederatedLoginService}, such as full OpenID URL.
117 *
118 * @return must not be null.
119 */
120 public abstract String getIdentifier();
121
122 /**
123 * Gets a short ID of this user, as a suitable candidate for {@link User#getId()}.
124 * This should be Unix username like token.
125 *
126 * @return null if this information is not available.
127 */
128 public abstract String getNickname();
129
130 /**
131 * Gets a human readable full name of this user. Maps to {@link User#getDisplayName()}
132 *
133 * @return null if this information is not available.
134 */
135 public abstract String getFullName();
136
137 /**
138 * Gets the e-mail address of this user, like "abc@def.com"
139 *
140 * @return null if this information is not available.
141 */
142 public abstract String getEmailAddress();
143
144 /**
145 * Returns a human-readable pronoun that describes this kind of identifier.
146 * This is used for rendering UI. For example, "OpenID", "Twitter ID", etc.
147 */
148 public abstract String getPronoun();
149
150 /**
151 * Locates the user who owns this identifier.
152 */
153 public final User locateUser() {
154 Class<? extends FederatedLoginServiceUserProperty> pt = getUserPropertyClass();
155 String id = getIdentifier();
156
157 for (User u : User.getAll()) {
158 if (u.getProperty(pt).has(id))
159 return u;
160 }
161 return null;
162 }
163
164 /**
165 * Call this method to authenticate the user when you confirmed (via your protocol specific work) that
166 * the current HTTP request indeed owns this identifier.
167 *
168 * <p>
169 * This method will locate the user who owns this identifier, associate the credential with
170 * the current session. IOW, it signs in the user.
171 *
172 * @throws UnclaimedIdentityException
173 * If this identifier is not claimed by anyone. If you just let this exception propagate
174 * to the caller of your "doXyz" method, it will either render an error page or initiate
175 * a user registration session (provided that {@link SecurityRealm} supports that.)
176 */
177 public User signin() throws UnclaimedIdentityException {
178 User u = locateUser();
179 if (u!=null) {
180 // login as this user
181 UserDetails d = Jenkins.getInstance().getSecurityRealm().loadUserByUsername(u.getId());
182
183 UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(d,"",d.getAuthorities());
184 token.setDetails(d);
185 SecurityContextHolder.getContext().setAuthentication(token);
186 return u;
187 } else {
188 // Unassociated identity.
189 throw new UnclaimedIdentityException(this);
190 }
191 }
192
193 /**
194 * Your implementation will call this method to add this identifier to the current user
195 * of an already authenticated session.
196 *
197 * <p>
198 * This method will record the identifier in {@link FederatedLoginServiceUserProperty} so that
199 * in the future the user can login to Hudson with the identifier.
200 */
201 public void addToCurrentUser() throws IOException {
202 User u = User.current();
203 if (u==null) throw new IllegalStateException("Current request is unauthenticated");
204
205 addTo(u);
206 }
207
208 /**
209 * Adds this identity to the specified user.
210 */
211 public void addTo(User u) throws IOException {
212 FederatedLoginServiceUserProperty p = u.getProperty(getUserPropertyClass());
213 if (p==null) {
214 p = (FederatedLoginServiceUserProperty) UserProperty.all().find(getUserPropertyClass()).newInstance(u);
215 u.addProperty(p);
216 }
217 p.addIdentifier(getIdentifier());
218 }
219
220 @Override
221 public String toString() {
222 return getIdentifier();
223 }
224 }
225
226 /**
227 * Used in {@link FederatedIdentity#signin()} to indicate that the identifier is not currently
228 * associated with anyone.
229 */
230 public static class UnclaimedIdentityException extends RuntimeException implements HttpResponse {
231 public final FederatedIdentity identity;
232
233 public UnclaimedIdentityException(FederatedIdentity identity) {
234 this.identity = identity;
235 }
236
237 public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
238 SecurityRealm sr = Jenkins.getInstance().getSecurityRealm();
239 if (sr.allowsSignup()) {
240 try {
241 sr.commenceSignup(identity).generateResponse(req,rsp,node);
242 return;
243 } catch (UnsupportedOperationException e) {
244 // fall through
245 }
246 }
247
248 // this security realm doesn't support user registration.
249 // just report an error
250 req.getView(this,"error").forward(req,rsp);
251 }
252 }
253
254 public static ExtensionList<FederatedLoginService> all() {
255 return Jenkins.getInstance().getExtensionList(FederatedLoginService.class);
256 }
257 }
258