提交 a7f34c0a 编写于 作者: K Kohsuke Kawaguchi

Merge branch 'federated-login-service'

* federated-login-service:
  show a message indicating why they are seeing the login screen
  extend from RuntimeException to simplify tunneling
  Oops
  honor UserPropertyDescriptor.isEnabled()
  relaxing this a bit
  report the user that was signed in.
  Added the registration screen for newly identified user.
  unclaimed identity should commence a signup sequence
  tweaking abstraction
  needs a title
  Added an extension point for external federated login service.
  still a work in progress. Federated login support
/*
* The MIT License
*
*
* Copyright (c) 2004-2010, Sun Microsystems, Inc., Kohsuke Kawaguchi,
* Erik Ramfelt, Koichi Fujikawa, Red Hat, Inc., Seiji Sogabe,
* Stephen Connolly, Tom Huybrechts, Yahoo! Inc., Alan Harder
*
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
......@@ -81,6 +81,7 @@ import hudson.security.ACL;
import hudson.security.AccessControlled;
import hudson.security.AuthorizationStrategy;
import hudson.security.BasicAuthenticationFilter;
import hudson.security.FederatedLoginService;
import hudson.security.HudsonFilter;
import hudson.security.LegacyAuthorizationStrategy;
import hudson.security.LegacySecurityRealm;
......@@ -263,7 +264,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
*/
// this field needs to be at the very top so that other components can look at this value even during unmarshalling
private String version = "1.0";
/**
* Number of executors of the master node.
*/
......@@ -419,9 +420,9 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
* This is {@link Integer} so that we can initialize it to '5' for upgrading users.
*/
/*package*/ Integer quietPeriod;
/**
* Global default for {@link AbstractProject#getScmCheckoutRetryCount()}
* Global default for {@link AbstractProject#getScmCheckoutRetryCount()}
*/
/*package*/ int scmCheckoutRetryCount;
......@@ -483,7 +484,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
* {@link hudson.security.csrf.CrumbIssuer}
*/
private volatile CrumbIssuer crumbIssuer;
/**
* All labels known to Hudson. This allows us to reuse the same label instances
* as much as possible, even though that's not a strict requirement.
......@@ -586,12 +587,12 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
throw new IllegalStateException("second instance");
theInstance = this;
// doing this early allows InitStrategy to set environment upfront
// doing this early allows InitStrategy to set environment upfront
final InitStrategy is = InitStrategy.get(Thread.currentThread().getContextClassLoader());
Trigger.timer = new Timer("Hudson cron thread");
queue = new Queue(CONSISTENT_HASH?LoadBalancer.CONSISTENT_HASH:LoadBalancer.DEFAULT);
try {
dependencyGraph = DependencyGraph.EMPTY;
} catch (InternalError e) {
......@@ -725,7 +726,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
*
* <p>
* At this point plugins are not loaded yet, so we fall back to the META-INF/services look up to discover implementations.
* As such there's no way for plugins to participate into this process.
* As such there's no way for plugins to participate into this process.
*/
private ReactorListener buildReactorListener() throws IOException {
List<ReactorListener> r = (List) Service.loadInstances(Thread.currentThread().getContextClassLoader(), InitReactorListener.class);
......@@ -959,7 +960,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
throw new AssertionError(type+" is missing its descriptor");
return d;
}
/**
* Gets the {@link Descriptor} instance in the current Hudson by its type.
*/
......@@ -1099,6 +1100,18 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
save();
}
public FederatedLoginService getFederatedLoginService(String name) {
for (FederatedLoginService fls : FederatedLoginService.all()) {
if (fls.getUrlName().equals(name))
return fls;
}
return null;
}
public List<FederatedLoginService> getFederatedLoginServices() {
return FederatedLoginService.all();
}
public Launcher createLauncher(TaskListener listener) {
return new LocalLauncher(listener).decorateFor(this);
}
......@@ -1214,7 +1227,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
if (item.hasPermission(Item.READ))
viewableItems.add(item);
}
return viewableItems;
}
......@@ -1222,7 +1235,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
* Returns the read-only view of all the {@link TopLevelItem}s keyed by their names.
* <p>
* This method is efficient, as it doesn't involve any copying.
*
*
* @since 1.296
*/
public Map<String,TopLevelItem> getItemMap() {
......@@ -1336,7 +1349,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
views.remove(view);
save();
}
public ViewsTabBar getViewsTabBar() {
return viewsTabBar;
}
......@@ -1659,15 +1672,15 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
public int getQuietPeriod() {
return quietPeriod!=null ? quietPeriod : 5;
}
/**
* Gets the global SCM check out retry count.
*/
public int getScmCheckoutRetryCount() {
return scmCheckoutRetryCount;
}
/**
* @deprecated
......@@ -1822,7 +1835,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
public boolean isUseCrumbs() {
return crumbIssuer!=null;
}
/**
* Returns the constant that captures the three basic security modes
* in Hudson.
......@@ -1954,7 +1967,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
*
* @return
* {@link InitMilestone#STARTED} even if the initialization hasn't been started, so that this method
* never returns null.
* never returns null.
*/
public InitMilestone getInitLevel() {
return initLevel;
......@@ -2463,7 +2476,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
label = json.optString("labelString","");
quietPeriod = json.getInt("quiet_period");
scmCheckoutRetryCount = json.getInt("retry_count");
systemMessage = Util.nullify(req.getParameter("system_message"));
......@@ -2501,7 +2514,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
public CrumbIssuer getCrumbIssuer() {
return crumbIssuer;
}
public void setCrumbIssuer(CrumbIssuer issuer) {
crumbIssuer = issuer;
}
......@@ -2897,7 +2910,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
// Parse the request
MultipartFormDataParser p = new MultipartFormDataParser(req);
if(Hudson.getInstance().isUseCrumbs() && !Hudson.getInstance().getCrumbIssuer().validateCrumb(req, p)) {
rsp.sendError(HttpServletResponse.SC_FORBIDDEN,"No crumb found");
rsp.sendError(HttpServletResponse.SC_FORBIDDEN,"No crumb found");
}
try {
rsp.sendRedirect2(req.getContextPath()+"/fingerprint/"+
......@@ -2999,7 +3012,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
* Queues up a restart of Hudson for when there are no builds running, if we can.
*
* This first replaces "app" to {@link HudsonIsRestarting}
*
*
* @since 1.332
*/
@CLIMethod(name="safe-restart")
......@@ -3055,7 +3068,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
lifecycle.verifyRestartable(); // verify that Hudson is restartable
// Quiet down so that we won't launch new builds.
isQuietingDown = true;
new Thread("safe-restart thread") {
final String exitUser = getAuthentication().getName();
@Override
......@@ -3105,7 +3118,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
System.exit(0);
}
/**
* Shutdown the system safely.
* @since 1.332
......@@ -3268,7 +3281,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
// this method can be used to check if a file exists anywhere in the file system,
// so it should be protected.
checkPermission(Item.CREATE);
if(fixEmpty(value)==null)
return FormValidation.ok();
......@@ -3470,6 +3483,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
|| rest.startsWith("/tcpSlaveAgentListener")
|| rest.startsWith("/cli")
|| rest.startsWith("/whoAmI")
|| rest.startsWith("/federatedLoginService/")
|| rest.startsWith("/securityRealm"))
return this; // URLs that are always visible without READ permission
throw e;
......@@ -3588,7 +3602,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
public static <T> T lookup(Class<T> type) {
return Hudson.getInstance().lookup.get(type);
}
/**
* @deprecated since 2007-12-18.
* Use {@link #checkPermission(Permission)}
......@@ -3764,7 +3778,7 @@ public final class Hudson extends Node implements ItemGroup<TopLevelItem>, Stapl
* Automatically try to launch a slave when Hudson is initialized or a new slave is created.
*/
public static boolean AUTOMATIC_SLAVE_LAUNCH = true;
private static final Logger LOGGER = Logger.getLogger(Hudson.class.getName());
private static final Pattern ICON_SIZE = Pattern.compile("\\d+x\\d+");
......
......@@ -443,15 +443,18 @@ public class User extends AbstractModelObject implements AccessControlled, Savea
List<UserProperty> props = new ArrayList<UserProperty>();
int i = 0;
for (UserPropertyDescriptor d : UserProperty.all()) {
JSONObject o = json.getJSONObject("userProperty" + (i++));
UserProperty p = getProperty(d.clazz);
if (p != null) {
p = p.reconfigure(req, o);
} else {
p = d.newInstance(req, o);
JSONObject o = json.optJSONObject("userProperty" + (i++));
if (o!=null) {
if (p != null) {
p = p.reconfigure(req, o);
} else {
p = d.newInstance(req, o);
}
p.setUser(this);
}
p.setUser(this);
props.add(p);
}
this.properties = props;
......
/*
* The MIT License
*
* Copyright (c) 2010, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.security;
import hudson.ExtensionList;
import hudson.ExtensionPoint;
import hudson.model.Hudson;
import hudson.model.User;
import hudson.model.UserProperty;
import org.acegisecurity.context.SecurityContextHolder;
import org.acegisecurity.providers.UsernamePasswordAuthenticationToken;
import org.acegisecurity.userdetails.UserDetails;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import javax.servlet.ServletException;
import java.io.IOException;
/**
* Abstraction for a login mechanism through external authenticator/identity provider
* (instead of username/password.)
*
* <p>
* This extension point adds additional login mechanism for {@link SecurityRealm}s that
* authenticate the user via username/password (which typically extends from {@link AbstractPasswordBasedSecurityRealm}.)
* The intended use case is protocols like OpenID, OAuth, and other SSO-like services.
*
* <p>
* The basic abstraction is that:
*
* <ul>
* <li>
* The user can have (possibly multiple, possibly zero) opaque strings to their {@linkplain User} object.
* Such opaque strings are called "identifiers."
* Think of them as OpenID URLs, twitter account names, etc.
* Identifiers are only comparable within the same {@link FederatedLoginService} implementation.
*
* <li>
* After getting authenticated by some means, the user can add additional identifiers to their account.
* Your implementation would do protocol specific thing to verify that the user indeed owns the claimed identifier,
* create a {@link FederatedIdentity} instance,
* then call {@link FederatedIdentity#addToCurrentUser()} to record such association.
*
* <li>
* In the login page, instead of entering the username and password, the user opts for authenticating
* via other services. Think of OpenID, OAuth, your corporate SSO service, etc.
* The user proves (by your protocol specific way) that they own some identifier, then
* create a {@link FederatedIdentity} instance, and invoke {@link FederatedIdentity#signin()} to sign in that user.
*
* </ul>
*
*
* <h2>Views</h2>
* <dl>
* <dt>loginFragment.jelly
* <dd>
* Injected into the login form page, after the default "login" button but before
* the "create account" link. Use this to generate a button or a link so that the user
* can initiate login via your federated login service.
* </dl>
*
* <h2>URL Binding</h2>
* <p>
* Each {@link FederatedLoginService} is exposed to the URL space via {@link Hudson#getFederatedLoginService(String)}.
* So for example if your {@linkplain #getUrlName() url name} is "openid", this object gets
* "/federatedLoginService/openid" as the URL.
*
* @author Kohsuke Kawaguchi
* @since 1.394
*/
public abstract class FederatedLoginService implements ExtensionPoint {
/**
* Returns the url name that determines where this {@link FederatedLoginService} is mapped to in the URL space.
*
* <p>
* The object is bound to /federatedLoginService/URLNAME/. The url name needs to be unique among all
* {@link FederatedLoginService}s.
*/
public abstract String getUrlName();
/**
* Returns your implementation of {@link FederatedLoginServiceUserProperty} that stores
* opaque identifiers.
*/
public abstract Class<? extends FederatedLoginServiceUserProperty> getUserPropertyClass();
/**
* Identity information as obtained from {@link FederatedLoginService}.
*/
public abstract class FederatedIdentity {
/**
* Gets the string representation of the identity in the form that makes sense to the enclosing
* {@link FederatedLoginService}, such as full OpenID URL.
*
* @return must not be null.
*/
public abstract String getIdentifier();
/**
* Gets a short ID of this user, as a suitable candidate for {@link User#getId()}.
* This should be Unix username like token.
*
* @return null if this information is not available.
*/
public abstract String getNickname();
/**
* Gets a human readable full name of this user. Maps to {@link User#getDisplayName()}
*
* @return null if this information is not available.
*/
public abstract String getFullName();
/**
* Gets the e-mail address of this user, like "abc@def.com"
*
* @return null if this information is not available.
*/
public abstract String getEmailAddress();
/**
* Returns a human-readable pronoun that describes this kind of identifier.
* This is used for rendering UI. For example, "OpenID", "Twitter ID", etc.
*/
public abstract String getPronoun();
/**
* Locates the user who owns this identifier.
*/
public final User locateUser() {
Class<? extends FederatedLoginServiceUserProperty> pt = getUserPropertyClass();
String id = getIdentifier();
for (User u : User.getAll()) {
if (u.getProperty(pt).has(id))
return u;
}
return null;
}
/**
* Call this method to authenticate the user when you confirmed (via your protocol specific work) that
* the current HTTP request indeed owns this identifier.
*
* <p>
* This method will locate the user who owns this identifier, associate the credential with
* the current session. IOW, it signs in the user.
*
* @throws UnclaimedIdentityException
* If this identifier is not claimed by anyone. If you just let this exception propagate
* to the caller of your "doXyz" method, it will either render an error page or initiate
* a user registration session (provided that {@link SecurityRealm} supports that.)
*/
public User signin() throws UnclaimedIdentityException {
User u = locateUser();
if (u!=null) {
// login as this user
UserDetails d = Hudson.getInstance().getSecurityRealm().loadUserByUsername(u.getId());
UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(d,"",d.getAuthorities());
token.setDetails(d);
SecurityContextHolder.getContext().setAuthentication(token);
return u;
} else {
// Unassociated identity.
throw new UnclaimedIdentityException(this);
}
}
/**
* Your implementation will call this method to add this identifier to the current user
* of an already authenticated session.
*
* <p>
* This method will record the identifier in {@link FederatedLoginServiceUserProperty} so that
* in the future the user can login to Hudson with the identifier.
*/
public void addToCurrentUser() throws IOException {
User u = User.current();
if (u==null) throw new IllegalStateException("Current request is unauthenticated");
addTo(u);
}
/**
* Adds this identity to the specified user.
*/
public void addTo(User u) throws IOException {
FederatedLoginServiceUserProperty p = u.getProperty(getUserPropertyClass());
if (p==null) {
p = (FederatedLoginServiceUserProperty) UserProperty.all().find(getUserPropertyClass()).newInstance(u);
u.addProperty(p);
}
p.addIdentifier(getIdentifier());
}
@Override
public String toString() {
return getIdentifier();
}
}
/**
* Used in {@link FederatedIdentity#signin()} to indicate that the identifier is not currently
* associated with anyone.
*/
public static class UnclaimedIdentityException extends RuntimeException implements HttpResponse {
public final FederatedIdentity identity;
public UnclaimedIdentityException(FederatedIdentity identity) {
this.identity = identity;
}
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
SecurityRealm sr = Hudson.getInstance().getSecurityRealm();
if (sr.allowsSignup()) {
try {
sr.commenceSignup(identity).generateResponse(req,rsp,node);
return;
} catch (UnsupportedOperationException e) {
// fall through
}
}
// this security realm doesn't support user registration.
// just report an error
req.getView(this,"error").forward(req,rsp);
}
}
public static ExtensionList<FederatedLoginService> all() {
return Hudson.getInstance().getExtensionList(FederatedLoginService.class);
}
}
/*
* The MIT License
*
* Copyright (c) 2010, CloudBees, Inc.
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in
* all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
* THE SOFTWARE.
*/
package hudson.security;
import hudson.model.UserProperty;
import java.io.IOException;
import java.util.Collection;
import java.util.Collections;
import java.util.HashSet;
import java.util.Set;
/**
*
* @author Kohsuke Kawaguchi
* @since 1.394
* @see FederatedLoginService
*/
public class FederatedLoginServiceUserProperty extends UserProperty {
protected final Set<String> identifiers;
protected FederatedLoginServiceUserProperty(Collection<String> identifiers) {
this.identifiers = new HashSet<String>(identifiers);
}
public boolean has(String identifier) {
return identifiers.contains(identifier);
}
public Collection<String> getIdentifiers() {
return Collections.unmodifiableSet(identifiers);
}
public synchronized void addIdentifier(String id) throws IOException {
identifiers.add(id);
user.save();
}
}
......@@ -34,6 +34,7 @@ import hudson.model.ModelObject;
import hudson.model.User;
import hudson.model.UserProperty;
import hudson.model.UserPropertyDescriptor;
import hudson.security.FederatedLoginService.FederatedIdentity;
import hudson.tasks.Mailer;
import hudson.util.PluginServletFilter;
import hudson.util.Protector;
......@@ -51,6 +52,9 @@ import org.acegisecurity.providers.encoding.ShaPasswordEncoder;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.kohsuke.stapler.DataBoundConstructor;
import org.kohsuke.stapler.ForwardToView;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.HttpResponses;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
......@@ -147,21 +151,56 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
return u;
}
/**
* Show the sign up page with the data from the identity.
*/
@Override
public HttpResponse commenceSignup(final FederatedIdentity identity) {
// store the identity in the session so that we can use this later
Stapler.getCurrentRequest().getSession().setAttribute(FEDERATED_IDENTITY_SESSION_KEY,identity);
return new ForwardToView(this,"signupWithFederatedIdentity.jelly") {
@Override
public void generateResponse(StaplerRequest req, StaplerResponse rsp, Object node) throws IOException, ServletException {
SignupInfo si = new SignupInfo(identity);
si.errorMessage = Messages.HudsonPrivateSecurityRealm_WouldYouLikeToSignUp(identity.getPronoun(),identity.getIdentifier());
req.setAttribute("data", si);
super.generateResponse(req, rsp, node);
}
};
}
/**
* Creates an account and associates that with the given identity. Used in conjunction
* with {@link #commenceSignup(FederatedIdentity)}.
*/
public User doCreateAccountWithFederatedIdentity(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
User u = _doCreateAccount(req,rsp,"signupWithFederatedIdentity.jelly");
if (u!=null)
((FederatedIdentity)req.getSession().getAttribute(FEDERATED_IDENTITY_SESSION_KEY)).addTo(u);
return u;
}
private static final String FEDERATED_IDENTITY_SESSION_KEY = HudsonPrivateSecurityRealm.class.getName()+".federatedIdentity";
/**
* Creates an user account. Used for self-registration.
*/
public void doCreateAccount(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
if(!allowsSignup()) {
rsp.sendError(SC_UNAUTHORIZED,"User sign up is prohibited");
return;
}
public User doCreateAccount(StaplerRequest req, StaplerResponse rsp) throws IOException, ServletException {
return _doCreateAccount(req, rsp, "signup.jelly");
}
private User _doCreateAccount(StaplerRequest req, StaplerResponse rsp, String formView) throws ServletException, IOException {
if(!allowsSignup())
throw HttpResponses.error(SC_UNAUTHORIZED,new Exception("User sign up is prohibited"));
boolean firstUser = !hasSomeUser();
User u = createAccount(req, rsp, true, "signup.jelly");
User u = createAccount(req, rsp, true, formView);
if(u!=null) {
if(firstUser)
tryToMakeAdmin(u); // the first user should be admin, or else there's a risk of lock out
loginAndTakeBack(req, rsp, u);
}
return u;
}
/**
......@@ -227,8 +266,7 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
private User createAccount(StaplerRequest req, StaplerResponse rsp, boolean selfRegistration, String formView) throws ServletException, IOException {
// form field validation
// this pattern needs to be generalized and moved to stapler
SignupInfo si = new SignupInfo();
req.bindParameters(si);
SignupInfo si = new SignupInfo(req);
if(selfRegistration && !validateCaptcha(si.captcha))
si.errorMessage = "Text didn't match the word shown in the image";
......@@ -328,6 +366,19 @@ public class HudsonPrivateSecurityRealm extends AbstractPasswordBasedSecurityRea
* To display an error message, set it here.
*/
public String errorMessage;
public SignupInfo() {
}
public SignupInfo(StaplerRequest req) {
req.bindParameters(this);
}
public SignupInfo(FederatedIdentity i) {
this.username = i.getNickname();
this.fullname = i.getFullName();
this.email = i.getEmailAddress();
}
}
/**
......
......@@ -33,6 +33,7 @@ import hudson.cli.CLICommand;
import hudson.model.AbstractDescribableImpl;
import hudson.model.Descriptor;
import hudson.model.Hudson;
import hudson.security.FederatedLoginService.FederatedIdentity;
import hudson.util.DescriptorList;
import hudson.util.PluginServletFilter;
import hudson.util.spring.BeanBuilder;
......@@ -47,6 +48,7 @@ import static org.acegisecurity.ui.rememberme.TokenBasedRememberMeServices.ACEGI
import org.acegisecurity.userdetails.UserDetailsService;
import org.acegisecurity.userdetails.UserDetails;
import org.acegisecurity.userdetails.UsernameNotFoundException;
import org.kohsuke.stapler.HttpResponse;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
......@@ -299,6 +301,28 @@ public abstract class SecurityRealm extends AbstractDescribableImpl<SecurityReal
throw new UserMayOrMayNotExistException(groupname);
}
/**
* Starts the user registration process for a new user that has the given verified identity.
*
* <p>
* If the user logs in through a {@link FederatedLoginService}, verified that the current user
* owns an {@linkplain FederatedIdentity identity}, but no existing user account has claimed that identity,
* then this method is invoked.
*
* <p>
* The expected behaviour is to confirm that the user would like to create a new account, and
* associate this federated identity to the newly created account (via {@link FederatedIdentity#addToCurrentUser()}.
*
* @throws UnsupportedOperationException
* If this implementation doesn't support the signup through this mechanism.
* This is the default implementation.
*
* @since 1.394
*/
public HttpResponse commenceSignup(FederatedIdentity identity) {
throw new UnsupportedOperationException();
}
/**
* {@link DefaultManageableImageCaptchaService} holder to defer initialization.
*/
......
......@@ -55,6 +55,12 @@ THE SOFTWARE.
</script>
</form>
<j:forEach var="fls" items="${it.getFederatedLoginServices()}">
<div>
<st:include page="loginFragment.jelly" it="${fls}"/>
</div>
</j:forEach>
<j:if test="${it.securityRealm.allowsSignup()}">
<div style="margin-top:2em">
${%signUp}
......
......@@ -42,14 +42,16 @@ THE SOFTWARE.
<j:invokeStatic var="descriptors" className="hudson.model.UserProperty" method="all" />
<j:set var="instances" value="${it.properties}" />
<j:forEach var="d" items="${descriptors}" varStatus="loop">
<f:section title="${d.displayName}">
<j:set var="descriptor" value="${d}" />
<j:set var="instance" value="${instances[d]}" />
<f:rowSet name="userProperty${loop.index}">
<st:include from="${d}" page="${d.configPage}"/>
</f:rowSet>
</f:section>
<j:if test="${d.enabled}">
<f:section title="${d.displayName}">
<j:set var="descriptor" value="${d}" />
<j:set var="instance" value="${instances[d]}" />
<f:rowSet name="userProperty${loop.index}">
<st:include from="${d}" page="${d.configPage}"/>
</f:rowSet>
</f:section>
</j:if>
</j:forEach>
<f:block>
......
<!--
The MIT License
Copyright (c) 2010, CloudBees, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<!--
This is used to create the first user.
-->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<st:statusCode value="403" />
<l:layout title="${%loginError(it.identifier.pronoun)}">
<l:main-panel>
<h1>${%loginError(it.identifier.pronoun)}</h1>
<p>${%blurb(it.identifier.pronoun, it.identifier.identifier)}</p>
</l:main-panel>
</l:layout>
</j:jelly>
\ No newline at end of file
loginError=Login Error: unassociated {0}
blurb={0} "{1}" is not associated with any of the Hudson user account. \
Note that if you already have a user account and is trying to associate a {0} with your account, \
this is a wrong place. To do so, <ol><li>Login <li>Click on your name <li>Click on the "Configure" link, and \
then <li>associate a new {0} from that page</ol>
......@@ -24,7 +24,7 @@ THE SOFTWARE.
<!-- tag file sed by both signup.jelly and addUser.jelly -->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<l:layout norefresh="true">
<l:layout norefresh="true" title="${%Sign up}">
<l:header>
<style>
<!-- match width with captcha image -->
......
<!--
The MIT License
Copyright (c) 2010, CloudBees, Inc.
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.
-->
<!--
User self sign up page.
-->
<j:jelly xmlns:j="jelly:core" xmlns:st="jelly:stapler" xmlns:d="jelly:define" xmlns:l="/lib/layout" xmlns:t="/lib/hudson" xmlns:f="/lib/form">
<local:_entryForm host="${app}" title="${%Sign up}" action="createAccountWithFederatedIdentity" captcha="${true}" xmlns:local="/hudson/security/HudsonPrivateSecurityRealm" />
</j:jelly>
\ No newline at end of file
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, Inc. Kohsuke Kawaguchi. Knud Poulsen.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Sign\ up=Tilmelding
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Simon Wiest
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Sign\ up=Registrieren
# The MIT License
#
# Copyright (c) 2004-2010, Sun Microsystems, Inc.
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Sign\ up=Registrarse
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Eric Lefevre-Ardant
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Sign\ up=S''inscrire
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Seiji Sogabe
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Sign\ up=\u30b5\u30a4\u30f3\u30a2\u30c3\u30d7
\ No newline at end of file
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Reginaldo L. Russinholi, Cleiber Silva
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Sign\ up=Inscrever
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Oguz Dag
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Sign\ up=Kay\u0131t ol
# The MIT License
#
# Copyright (c) 2004-2009, Sun Microsystems, Inc., Kohsuke Kawaguchi, Simon Wiest
#
# Permission is hereby granted, free of charge, to any person obtaining a copy
# of this software and associated documentation files (the "Software"), to deal
# in the Software without restriction, including without limitation the rights
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
# copies of the Software, and to permit persons to whom the Software is
# furnished to do so, subject to the following conditions:
#
# The above copyright notice and this permission notice shall be included in
# all copies or substantial portions of the Software.
#
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
# THE SOFTWARE.
Sign\ up=\u6ce8\u518c
......@@ -22,6 +22,7 @@
GlobalMatrixAuthorizationStrategy.DisplayName=Matrix-based security
HudsonPrivateSecurityRealm.WouldYouLikeToSignUp=This {0} {1} is new to Hudson. Would you like to sign up?
LegacyAuthorizationStrategy.DisplayName=Legacy mode
HudsonPrivateSecurityRealm.DisplayName=Hudson''s own user database
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册