Remove login dialogs and replace with /login/ URLs
Insetad of handling OpenID and username/password authentication in the GWT UI redirect all forms of authentication through /login/. This allows the server to return normal HTML to handle the user sign-in process. By using typical form based HTML for username and password login browsers can offer the choice to save this data locally in the user's password store. Using /login/ for all authentication simplifies some UI code and may make things easier for plugins. A plugin only needs to do a redirect to /login/ to upgrade an anonymous user to be an authenticated one. Change-Id: I0d7a59693a257c5da8bab19e3ef0a627f8a41c6e
This commit is contained in:
		| @@ -1,30 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.common.auth.openid; | ||||
|  | ||||
| import com.google.gerrit.common.auth.SignInMode; | ||||
| import com.google.gwtjsonrpc.common.AsyncCallback; | ||||
| import com.google.gwtjsonrpc.common.AllowCrossSiteRequest; | ||||
| import com.google.gwtjsonrpc.common.RemoteJsonService; | ||||
| import com.google.gwtjsonrpc.common.RpcImpl; | ||||
| import com.google.gwtjsonrpc.common.RpcImpl.Version; | ||||
|  | ||||
| @RpcImpl(version = Version.V2_0) | ||||
| public interface OpenIdService extends RemoteJsonService { | ||||
|   @AllowCrossSiteRequest | ||||
|   void discover(String openidIdentifier, SignInMode mode, | ||||
|       boolean remember, String returnToken, | ||||
|       AsyncCallback<DiscoveryResult> callback); | ||||
| } | ||||
| @@ -1,53 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.common.auth.userpass; | ||||
|  | ||||
| import com.google.gerrit.reviewdb.client.AuthType; | ||||
|  | ||||
| public class LoginResult { | ||||
|   public boolean success; | ||||
|   public boolean isNew; | ||||
|  | ||||
|   protected AuthType authType; | ||||
|   protected Error error; | ||||
|  | ||||
|   protected LoginResult() { | ||||
|   } | ||||
|  | ||||
|   public LoginResult(final AuthType authType) { | ||||
|     this.authType = authType; | ||||
|   } | ||||
|  | ||||
|   public AuthType getAuthType() { | ||||
|     return authType; | ||||
|   } | ||||
|  | ||||
|   public void setError(final Error error) { | ||||
|     this.error = error; | ||||
|     success = error == null; | ||||
|   } | ||||
|  | ||||
|   public Error getError() { | ||||
|     return error; | ||||
|   } | ||||
|  | ||||
|   public static enum Error { | ||||
|     /** Username or password are invalid */ | ||||
|     INVALID_LOGIN, | ||||
|  | ||||
|     /** The authentication server is unavailable or the query to it timed out */ | ||||
|     AUTHENTICATION_UNAVAILABLE | ||||
|   } | ||||
| } | ||||
| @@ -1,30 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.common.auth.userpass; | ||||
|  | ||||
| import com.google.gerrit.common.audit.Audit; | ||||
| import com.google.gwtjsonrpc.common.AsyncCallback; | ||||
| import com.google.gwtjsonrpc.common.AllowCrossSiteRequest; | ||||
| import com.google.gwtjsonrpc.common.RemoteJsonService; | ||||
| import com.google.gwtjsonrpc.common.RpcImpl; | ||||
| import com.google.gwtjsonrpc.common.RpcImpl.Version; | ||||
|  | ||||
| @RpcImpl(version = Version.V2_0) | ||||
| public interface UserPassAuthService extends RemoteJsonService { | ||||
|   @Audit(action = "sign in", obfuscate={1}) | ||||
|   @AllowCrossSiteRequest | ||||
|   void authenticate(String username, String password, | ||||
|       AsyncCallback<LoginResult> callback); | ||||
| } | ||||
| @@ -14,7 +14,6 @@ | ||||
|  | ||||
| package com.google.gerrit.common.data; | ||||
|  | ||||
| import com.google.gerrit.common.auth.openid.OpenIdProviderPattern; | ||||
| import com.google.gerrit.reviewdb.client.Account; | ||||
| import com.google.gerrit.reviewdb.client.Account.FieldName; | ||||
| import com.google.gerrit.reviewdb.client.AccountGeneralPreferences.DownloadCommand; | ||||
| @@ -31,8 +30,6 @@ public class GerritConfig implements Cloneable { | ||||
|   protected String registerText; | ||||
|   protected String httpPasswordUrl; | ||||
|   protected String reportBugUrl; | ||||
|   protected String openIdSsoUrl; | ||||
|   protected List<OpenIdProviderPattern> allowedOpenIDs; | ||||
|  | ||||
|   protected GitwebConfig gitweb; | ||||
|   protected boolean useContributorAgreements; | ||||
| @@ -93,22 +90,6 @@ public class GerritConfig implements Cloneable { | ||||
|     httpPasswordUrl = url; | ||||
|   } | ||||
|  | ||||
|   public String getOpenIdSsoUrl() { | ||||
|       return openIdSsoUrl; | ||||
|   } | ||||
|  | ||||
|   public void setOpenIdSsoUrl(final String u) { | ||||
|     openIdSsoUrl = u; | ||||
|   } | ||||
|  | ||||
|   public List<OpenIdProviderPattern> getAllowedOpenIDs() { | ||||
|     return allowedOpenIDs; | ||||
|   } | ||||
|  | ||||
|   public void setAllowedOpenIDs(List<OpenIdProviderPattern> l) { | ||||
|     allowedOpenIDs = l; | ||||
|   } | ||||
|  | ||||
|   public AuthType getAuthType() { | ||||
|     return authType; | ||||
|   } | ||||
|   | ||||
| @@ -60,8 +60,6 @@ import com.google.gerrit.client.admin.ProjectDashboardsScreen; | ||||
| import com.google.gerrit.client.admin.ProjectInfoScreen; | ||||
| import com.google.gerrit.client.admin.ProjectListScreen; | ||||
| import com.google.gerrit.client.admin.ProjectScreen; | ||||
| import com.google.gerrit.client.auth.openid.OpenIdSignInDialog; | ||||
| import com.google.gerrit.client.auth.userpass.UserPassSignInDialog; | ||||
| import com.google.gerrit.client.changes.AccountDashboardScreen; | ||||
| import com.google.gerrit.client.changes.ChangeScreen; | ||||
| import com.google.gerrit.client.changes.CustomDashboardScreen; | ||||
| @@ -78,7 +76,6 @@ import com.google.gerrit.client.rpc.GerritCallback; | ||||
| import com.google.gerrit.client.rpc.RestApi; | ||||
| import com.google.gerrit.client.ui.Screen; | ||||
| import com.google.gerrit.common.PageLinks; | ||||
| import com.google.gerrit.common.auth.SignInMode; | ||||
| import com.google.gerrit.common.data.PatchSetDetail; | ||||
| import com.google.gerrit.reviewdb.client.Account; | ||||
| import com.google.gerrit.reviewdb.client.AccountGroup; | ||||
| @@ -633,32 +630,6 @@ public class Dispatcher { | ||||
|         if (matchPrefix("/VE/", token) || matchPrefix("VE,", token)) | ||||
|           return new ValidateEmailScreen(skip(token)); | ||||
|  | ||||
|         if (matchPrefix("/SignInFailure,", token)) { | ||||
|           final String[] args = skip(token).split(","); | ||||
|           final SignInMode mode = SignInMode.valueOf(args[0]); | ||||
|           final String msg = KeyUtil.decode(args[1]); | ||||
|           final String to = MINE; | ||||
|           switch (Gerrit.getConfig().getAuthType()) { | ||||
|             case OPENID: | ||||
|               new OpenIdSignInDialog(mode, to, msg).center(); | ||||
|               break; | ||||
|             case LDAP: | ||||
|             case LDAP_BIND: | ||||
|               new UserPassSignInDialog(to, msg).center(); | ||||
|               break; | ||||
|             default: | ||||
|               return null; | ||||
|           } | ||||
|           switch (mode) { | ||||
|             case SIGN_IN: | ||||
|               return QueryScreen.forQuery("status:open"); | ||||
|             case LINK_IDENTIY: | ||||
|               return new MyIdentitiesScreen(); | ||||
|             case REGISTER: | ||||
|               break; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         if (matchExact(SETTINGS_NEW_AGREEMENT, token)) | ||||
|           return new NewAgreementScreen(); | ||||
|  | ||||
|   | ||||
| @@ -15,14 +15,11 @@ | ||||
| package com.google.gerrit.client; | ||||
|  | ||||
| import static com.google.gerrit.common.data.GlobalCapability.ADMINISTRATE_SERVER; | ||||
| import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT; | ||||
| import static com.google.gerrit.common.data.GlobalCapability.CREATE_GROUP; | ||||
| import static com.google.gerrit.common.data.GlobalCapability.CREATE_PROJECT; | ||||
|  | ||||
| import com.google.gerrit.client.account.AccountCapabilities; | ||||
| import com.google.gerrit.client.admin.ProjectScreen; | ||||
| import com.google.gerrit.client.auth.openid.OpenIdSignInDialog; | ||||
| import com.google.gerrit.client.auth.openid.OpenIdSsoPanel; | ||||
| import com.google.gerrit.client.auth.userpass.UserPassSignInDialog; | ||||
| import com.google.gerrit.client.changes.ChangeConstants; | ||||
| import com.google.gerrit.client.changes.ChangeListScreen; | ||||
| import com.google.gerrit.client.patches.PatchScreen; | ||||
| @@ -35,7 +32,6 @@ import com.google.gerrit.client.ui.Screen; | ||||
| import com.google.gerrit.client.ui.ScreenLoadEvent; | ||||
| import com.google.gerrit.common.ClientVersion; | ||||
| import com.google.gerrit.common.PageLinks; | ||||
| import com.google.gerrit.common.auth.SignInMode; | ||||
| import com.google.gerrit.common.data.GerritConfig; | ||||
| import com.google.gerrit.common.data.GitwebConfig; | ||||
| import com.google.gerrit.common.data.HostPageData; | ||||
| @@ -71,9 +67,9 @@ import com.google.gwt.user.client.Window; | ||||
| import com.google.gwt.user.client.Window.Location; | ||||
| import com.google.gwt.user.client.ui.Anchor; | ||||
| import com.google.gwt.user.client.ui.FlowPanel; | ||||
| import com.google.gwt.user.client.ui.FocusPanel; | ||||
| import com.google.gwt.user.client.ui.Grid; | ||||
| import com.google.gwt.user.client.ui.HTMLTable.CellFormatter; | ||||
| import com.google.gwt.user.client.ui.FocusPanel; | ||||
| import com.google.gwt.user.client.ui.InlineHTML; | ||||
| import com.google.gwt.user.client.ui.InlineLabel; | ||||
| import com.google.gwt.user.client.ui.Label; | ||||
| @@ -273,34 +269,7 @@ public class Gerrit implements EntryPoint { | ||||
|  | ||||
|   /** Sign the user into the application. */ | ||||
|   public static void doSignIn(String token) { | ||||
|     switch (myConfig.getAuthType()) { | ||||
|       case HTTP: | ||||
|       case HTTP_LDAP: | ||||
|       case CLIENT_SSL_CERT_LDAP: | ||||
|       case CUSTOM_EXTENSION: | ||||
|     Location.assign(loginRedirect(token)); | ||||
|         break; | ||||
|  | ||||
|       case DEVELOPMENT_BECOME_ANY_ACCOUNT: | ||||
|         Location.assign(selfRedirect("/become")); | ||||
|         break; | ||||
|  | ||||
|       case OPENID_SSO: | ||||
|         final RootPanel gBody = RootPanel.get("gerrit_body"); | ||||
|         OpenIdSsoPanel singleSignOnPanel = new OpenIdSsoPanel(); | ||||
|         gBody.add(singleSignOnPanel); | ||||
|         singleSignOnPanel.authenticate(SignInMode.SIGN_IN, token); | ||||
|         break; | ||||
|  | ||||
|       case OPENID: | ||||
|         new OpenIdSignInDialog(SignInMode.SIGN_IN, token, null).center(); | ||||
|         break; | ||||
|  | ||||
|       case LDAP: | ||||
|       case LDAP_BIND: | ||||
|         new UserPassSignInDialog(token, null).center(); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   public static String loginRedirect(String token) { | ||||
| @@ -726,8 +695,11 @@ public class Gerrit implements EntryPoint { | ||||
|         case OPENID: | ||||
|           menuRight.addItem(C.menuRegister(), new Command() { | ||||
|             public void execute() { | ||||
|               final String to = History.getToken(); | ||||
|               new OpenIdSignInDialog(SignInMode.REGISTER, to, null).center(); | ||||
|               String t = History.getToken(); | ||||
|               if (t == null) { | ||||
|                 t = ""; | ||||
|               } | ||||
|               doSignIn(PageLinks.REGISTER + t); | ||||
|             } | ||||
|           }); | ||||
|           menuRight.addItem(C.menuSignIn(), new Command() { | ||||
| @@ -760,7 +732,7 @@ public class Gerrit implements EntryPoint { | ||||
|           break; | ||||
|  | ||||
|         case DEVELOPMENT_BECOME_ANY_ACCOUNT: | ||||
|           menuRight.add(anchor("Become", selfRedirect("/become"))); | ||||
|           menuRight.add(anchor("Become", loginRedirect(""))); | ||||
|           break; | ||||
|       } | ||||
|     } | ||||
|   | ||||
| @@ -1,57 +0,0 @@ | ||||
| // Copyright (C) 2008 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.client; | ||||
|  | ||||
| import com.google.gerrit.common.auth.SignInMode; | ||||
| import com.google.gwtexpui.globalkey.client.GlobalKey; | ||||
| import com.google.gwtexpui.user.client.AutoCenterDialogBox; | ||||
|  | ||||
| /** Prompts the user to sign in to their account. */ | ||||
| public abstract class SignInDialog extends AutoCenterDialogBox { | ||||
|   protected final SignInMode mode; | ||||
|   protected final String token; | ||||
|  | ||||
|   /** | ||||
|    * Create a new dialog to handle user sign in. | ||||
|    * | ||||
|    * @param signInMode type of mode the login will perform. | ||||
|    * @param token the token to jump to after sign-in is complete. | ||||
|    */ | ||||
|   protected SignInDialog(final SignInMode signInMode, final String token) { | ||||
|     super(/* auto hide */true, /* modal */true); | ||||
|     setGlassEnabled(true); | ||||
|  | ||||
|     this.mode = signInMode; | ||||
|     this.token = token; | ||||
|  | ||||
|     switch (signInMode) { | ||||
|       case LINK_IDENTIY: | ||||
|         setText(Gerrit.C.linkIdentityDialogTitle()); | ||||
|         break; | ||||
|       case REGISTER: | ||||
|         setText(Gerrit.C.registerDialogTitle()); | ||||
|         break; | ||||
|       default: | ||||
|         setText(Gerrit.C.signInDialogTitle()); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void show() { | ||||
|     super.show(); | ||||
|     GlobalKey.dialog(this); | ||||
|   } | ||||
| } | ||||
| @@ -15,19 +15,19 @@ | ||||
| package com.google.gerrit.client.account; | ||||
|  | ||||
| import com.google.gerrit.client.Gerrit; | ||||
| import com.google.gerrit.client.auth.openid.OpenIdSignInDialog; | ||||
| import com.google.gerrit.client.auth.openid.OpenIdUtil; | ||||
| import com.google.gerrit.client.rpc.GerritCallback; | ||||
| import com.google.gerrit.client.rpc.ScreenLoadCallback; | ||||
| import com.google.gerrit.client.ui.FancyFlexTable; | ||||
| import com.google.gerrit.common.auth.SignInMode; | ||||
| import com.google.gerrit.common.auth.openid.OpenIdUrls; | ||||
| import com.google.gerrit.reviewdb.client.AccountExternalId; | ||||
| import com.google.gerrit.reviewdb.client.AuthType; | ||||
| import com.google.gwt.event.dom.client.ClickEvent; | ||||
| import com.google.gwt.event.dom.client.ClickHandler; | ||||
| import com.google.gwt.event.logical.shared.ValueChangeEvent; | ||||
| import com.google.gwt.event.logical.shared.ValueChangeHandler; | ||||
| import com.google.gwt.user.client.History; | ||||
| import com.google.gwt.user.client.Window.Location; | ||||
| import com.google.gwt.user.client.ui.Button; | ||||
| import com.google.gwt.user.client.ui.CheckBox; | ||||
| import com.google.gwt.user.client.ui.FlexTable.FlexCellFormatter; | ||||
| @@ -59,29 +59,15 @@ public class MyIdentitiesScreen extends SettingsScreen { | ||||
|     }); | ||||
|     add(deleteIdentity); | ||||
|  | ||||
|     switch (Gerrit.getConfig().getAuthType()) { | ||||
|       case OPENID: { | ||||
|         final Button linkIdentity = new Button(Util.C.buttonLinkIdentity()); | ||||
|     if (Gerrit.getConfig().getAuthType() == AuthType.OPENID) { | ||||
|       Button linkIdentity = new Button(Util.C.buttonLinkIdentity()); | ||||
|       linkIdentity.addClickHandler(new ClickHandler() { | ||||
|         @Override | ||||
|         public void onClick(final ClickEvent event) { | ||||
|             final String to = History.getToken(); | ||||
|             new OpenIdSignInDialog(SignInMode.LINK_IDENTIY, to, null).center(); | ||||
|           Location.assign(Gerrit.loginRedirect(History.getToken()) + "?link"); | ||||
|         } | ||||
|       }); | ||||
|       add(linkIdentity); | ||||
|         break; | ||||
|       } | ||||
|  | ||||
|       case CLIENT_SSL_CERT_LDAP: | ||||
|       case CUSTOM_EXTENSION: | ||||
|       case DEVELOPMENT_BECOME_ANY_ACCOUNT: | ||||
|       case HTTP: | ||||
|       case HTTP_LDAP: | ||||
|       case LDAP: | ||||
|       case LDAP_BIND: | ||||
|       case OPENID_SSO: | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   | ||||
| @@ -17,18 +17,6 @@ package com.google.gerrit.client.auth.openid; | ||||
| import com.google.gwt.i18n.client.Constants; | ||||
|  | ||||
| public interface OpenIdConstants extends Constants { | ||||
|   String buttonSignIn(); | ||||
|   String buttonRegister(); | ||||
|   String buttonLinkId(); | ||||
|  | ||||
|   String rememberMe(); | ||||
|  | ||||
|   String notAllowed(); | ||||
|   String noProvider(); | ||||
|   String error(); | ||||
|  | ||||
|   String nameGoogle(); | ||||
|   String nameYahoo(); | ||||
|  | ||||
|   String whatIsOpenIDHtml(); | ||||
| } | ||||
|   | ||||
| @@ -1,20 +1,2 @@ | ||||
| buttonSignIn = Sign In | ||||
| buttonRegister = Register | ||||
| buttonLinkId = Link Identity | ||||
|  | ||||
| rememberMe = Remember Me | ||||
|  | ||||
| notAllowed = Provider is not allowed. | ||||
| noProvider = Provider is not supported, or was incorrectly entered. | ||||
| error = Unable to connect with OpenID provider. | ||||
|  | ||||
| nameGoogle = Google Account | ||||
| nameYahoo = Yahoo! ID | ||||
|  | ||||
| whatIsOpenIDHtml = \ | ||||
|   <h2 class="smallHeading" style="margin-top: 25px;">What is OpenID?</h2>\ | ||||
|   <p>OpenID provides secure single-sign-on, without \ | ||||
|   revealing your passwords to this website.</p>\ | ||||
|   <p>There are many OpenID providers available.  You may already \ | ||||
|   be member of one!</p>\ | ||||
|   <p><a href="http://openid.net/get/" target="_blank">Get OpenID</a></p>\ | ||||
|   | ||||
| @@ -1,27 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.client.auth.openid; | ||||
|  | ||||
| import com.google.gwt.i18n.client.Messages; | ||||
|  | ||||
| public interface OpenIdMessages extends Messages { | ||||
|   String signInAt(String hostname); | ||||
|   String registerAt(String hostname); | ||||
|   String linkAt(String hostname); | ||||
|  | ||||
|   String signInWith(String who); | ||||
|   String registerWith(String who); | ||||
|   String linkWith(String who); | ||||
| } | ||||
| @@ -1,7 +0,0 @@ | ||||
| signInAt = Sign In to Gerrit Code Review at {0} | ||||
| registerAt = Register with Gerrit Code Review at {0} | ||||
| linkAt = Link Another Identity to Gerrit Code Review at {0} | ||||
|  | ||||
| signInWith = Sign in with a {0} | ||||
| registerWith = Register with a {0} | ||||
| linkWith = Link a {0} | ||||
| @@ -1,39 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.client.auth.openid; | ||||
|  | ||||
| import com.google.gwt.core.client.GWT; | ||||
| import com.google.gwt.resources.client.ClientBundle; | ||||
| import com.google.gwt.resources.client.DataResource; | ||||
| import com.google.gwt.resources.client.ImageResource; | ||||
|  | ||||
| interface OpenIdResources extends ClientBundle { | ||||
|   static final OpenIdResources I = GWT.create(OpenIdResources.class); | ||||
|  | ||||
|   @Source("openid.css") | ||||
|   OpenIdCss css(); | ||||
|  | ||||
|   @Source("identifierBackground.gif") | ||||
|   DataResource identifierBackground(); | ||||
|  | ||||
|   @Source("openidLogo.png") | ||||
|   ImageResource openidLogo(); | ||||
|  | ||||
|   @Source("iconGoogle.gif") | ||||
|   ImageResource iconGoogle(); | ||||
|  | ||||
|   @Source("iconYahoo.gif") | ||||
|   ImageResource iconYahoo(); | ||||
| } | ||||
| @@ -1,388 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.client.auth.openid; | ||||
|  | ||||
| import com.google.gerrit.client.Gerrit; | ||||
| import com.google.gerrit.client.SignInDialog; | ||||
| import com.google.gerrit.client.rpc.GerritCallback; | ||||
| import com.google.gerrit.client.ui.SmallHeading; | ||||
| import com.google.gerrit.common.auth.SignInMode; | ||||
| import com.google.gerrit.common.auth.openid.DiscoveryResult; | ||||
| import com.google.gerrit.common.auth.openid.OpenIdProviderPattern; | ||||
| import com.google.gerrit.common.auth.openid.OpenIdUrls; | ||||
| import com.google.gwt.core.client.Scheduler; | ||||
| import com.google.gwt.core.client.Scheduler.ScheduledCommand; | ||||
| import com.google.gwt.dom.client.FormElement; | ||||
| import com.google.gwt.event.dom.client.ClickEvent; | ||||
| import com.google.gwt.event.dom.client.ClickHandler; | ||||
| import com.google.gwt.event.dom.client.KeyCodes; | ||||
| import com.google.gwt.event.dom.client.KeyPressEvent; | ||||
| import com.google.gwt.event.dom.client.KeyPressHandler; | ||||
| import com.google.gwt.resources.client.ImageResource; | ||||
| import com.google.gwt.user.client.Cookies; | ||||
| import com.google.gwt.user.client.DOM; | ||||
| import com.google.gwt.user.client.Window; | ||||
| import com.google.gwt.user.client.ui.Anchor; | ||||
| import com.google.gwt.user.client.ui.Button; | ||||
| import com.google.gwt.user.client.ui.CheckBox; | ||||
| import com.google.gwt.user.client.ui.FlowPanel; | ||||
| import com.google.gwt.user.client.ui.FormPanel; | ||||
| import com.google.gwt.user.client.ui.FormPanel.SubmitEvent; | ||||
| import com.google.gwt.user.client.ui.FormSubmitCompleteEvent; | ||||
| import com.google.gwt.user.client.ui.HTML; | ||||
| import com.google.gwt.user.client.ui.Hidden; | ||||
| import com.google.gwt.user.client.ui.Image; | ||||
| import com.google.gwt.user.client.ui.InlineLabel; | ||||
| import com.google.gwtexpui.globalkey.client.NpTextBox; | ||||
|  | ||||
| import java.util.Map; | ||||
|  | ||||
| public class OpenIdSignInDialog extends SignInDialog implements | ||||
|     FormPanel.SubmitHandler { | ||||
|   static { | ||||
|     OpenIdResources.I.css().ensureInjected(); | ||||
|   } | ||||
|  | ||||
|   private final FlowPanel panelWidget; | ||||
|   private final FormPanel form; | ||||
|   private final FlowPanel formBody; | ||||
|   private final FormPanel redirectForm; | ||||
|   private final FlowPanel redirectBody; | ||||
|  | ||||
|   private FlowPanel errorLine; | ||||
|   private InlineLabel errorMsg; | ||||
|  | ||||
|   private Button login; | ||||
|   private NpTextBox providerId; | ||||
|   private CheckBox rememberId; | ||||
|   private boolean discovering; | ||||
|  | ||||
|   public OpenIdSignInDialog(final SignInMode requestedMode, final String token, | ||||
|       final String initialErrorMsg) { | ||||
|     super(requestedMode, token); | ||||
|  | ||||
|     formBody = new FlowPanel(); | ||||
|     formBody.setStyleName(OpenIdResources.I.css().loginForm()); | ||||
|  | ||||
|     form = new FormPanel(); | ||||
|     form.setMethod(FormPanel.METHOD_GET); | ||||
|     form.addSubmitHandler(this); | ||||
|     form.add(formBody); | ||||
|  | ||||
|     redirectBody = new FlowPanel(); | ||||
|     redirectBody.setVisible(false); | ||||
|     redirectForm = new FormPanel(); | ||||
|     redirectForm.add(redirectBody); | ||||
|  | ||||
|     panelWidget = new FlowPanel(); | ||||
|     panelWidget.add(form); | ||||
|     panelWidget.add(redirectForm); | ||||
|     add(panelWidget); | ||||
|  | ||||
|     createHeaderLogo(); | ||||
|     createHeaderText(); | ||||
|     createErrorBox(); | ||||
|     createIdentBox(); | ||||
|  | ||||
|     link(OpenIdUrls.URL_GOOGLE, OpenIdUtil.C.nameGoogle(), OpenIdResources.I | ||||
|         .iconGoogle()); | ||||
|     link(OpenIdUrls.URL_YAHOO, OpenIdUtil.C.nameYahoo(), OpenIdResources.I | ||||
|         .iconYahoo()); | ||||
|  | ||||
|     if (initialErrorMsg != null) { | ||||
|       showError(initialErrorMsg); | ||||
|     } | ||||
|     formBody.add(new HTML(OpenIdUtil.C.whatIsOpenIDHtml())); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void show() { | ||||
|     super.show(); | ||||
|     providerId.selectAll(); | ||||
|     Scheduler.get().scheduleDeferred(new ScheduledCommand() { | ||||
|       @Override | ||||
|       public void execute() { | ||||
|         providerId.setFocus(true); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   private void createHeaderLogo() { | ||||
|     final FlowPanel headerLogo = new FlowPanel(); | ||||
|     headerLogo.setStyleName(OpenIdResources.I.css().logo()); | ||||
|     headerLogo.add(new Image(OpenIdResources.I.openidLogo())); | ||||
|     formBody.add(headerLogo); | ||||
|   } | ||||
|  | ||||
|   private void createHeaderText() { | ||||
|     final FlowPanel headerText = new FlowPanel(); | ||||
|     final String me = Window.Location.getHostName(); | ||||
|     final SmallHeading headerLabel = new SmallHeading(); | ||||
|     switch (mode) { | ||||
|       case LINK_IDENTIY: | ||||
|         headerLabel.setText(OpenIdUtil.M.linkAt(me)); | ||||
|         break; | ||||
|       case REGISTER: | ||||
|         headerLabel.setText(OpenIdUtil.M.registerAt(me)); | ||||
|         break; | ||||
|       case SIGN_IN: | ||||
|       default: | ||||
|         headerLabel.setText(OpenIdUtil.M.signInAt(me)); | ||||
|         break; | ||||
|     } | ||||
|     headerText.add(headerLabel); | ||||
|     formBody.add(headerText); | ||||
|   } | ||||
|  | ||||
|   private void createErrorBox() { | ||||
|     errorLine = new FlowPanel(); | ||||
|     DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden"); | ||||
|     errorLine.setStyleName(OpenIdResources.I.css().error()); | ||||
|  | ||||
|     errorMsg = new InlineLabel(); | ||||
|     errorLine.add(errorMsg); | ||||
|     formBody.add(errorLine); | ||||
|   } | ||||
|  | ||||
|   private void showError(final String msgText) { | ||||
|     errorMsg.setText(msgText); | ||||
|     DOM.setStyleAttribute(errorLine.getElement(), "visibility", ""); | ||||
|   } | ||||
|  | ||||
|   private void hideError() { | ||||
|     DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden"); | ||||
|   } | ||||
|  | ||||
|   private void createIdentBox() { | ||||
|     boolean remember = mode == SignInMode.SIGN_IN || mode == SignInMode.REGISTER; | ||||
|  | ||||
|     final FlowPanel group = new FlowPanel(); | ||||
|     group.setStyleName(OpenIdResources.I.css().loginLine()); | ||||
|  | ||||
|     final FlowPanel line1 = new FlowPanel(); | ||||
|     group.add(line1); | ||||
|  | ||||
|     providerId = new NpTextBox(); | ||||
|     providerId.setVisibleLength(60); | ||||
|     providerId.setStyleName(OpenIdResources.I.css().identifier()); | ||||
|     providerId.setTabIndex(0); | ||||
|     providerId.addKeyPressHandler(new KeyPressHandler() { | ||||
|       @Override | ||||
|       public void onKeyPress(final KeyPressEvent event) { | ||||
|         if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) { | ||||
|           event.preventDefault(); | ||||
|           form.submit(); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|     line1.add(providerId); | ||||
|  | ||||
|     login = new Button(); | ||||
|     switch (mode) { | ||||
|       case LINK_IDENTIY: | ||||
|         login.setText(OpenIdUtil.C.buttonLinkId()); | ||||
|         break; | ||||
|       case REGISTER: | ||||
|         login.setText(OpenIdUtil.C.buttonRegister()); | ||||
|         break; | ||||
|       case SIGN_IN: | ||||
|       default: | ||||
|         login.setText(OpenIdUtil.C.buttonSignIn()); | ||||
|         break; | ||||
|     } | ||||
|     login.addClickHandler(new ClickHandler() { | ||||
|       @Override | ||||
|       public void onClick(final ClickEvent event) { | ||||
|         form.submit(); | ||||
|       } | ||||
|     }); | ||||
|     login.setTabIndex(remember ? 2 : 1); | ||||
|     line1.add(login); | ||||
|  | ||||
|     Button close = new Button(Gerrit.C.signInDialogClose()); | ||||
|     close.addClickHandler(new ClickHandler() { | ||||
|       @Override | ||||
|       public void onClick(ClickEvent event) { | ||||
|         hide(); | ||||
|       } | ||||
|     }); | ||||
|     close.setTabIndex(remember ? 3 : 2); | ||||
|     line1.add(close); | ||||
|  | ||||
|     if (remember) { | ||||
|       rememberId = new CheckBox(OpenIdUtil.C.rememberMe()); | ||||
|       rememberId.setTabIndex(1); | ||||
|       group.add(rememberId); | ||||
|  | ||||
|       String last = Cookies.getCookie(OpenIdUrls.LASTID_COOKIE); | ||||
|       if (last != null && !"".equals(last)) { | ||||
|         if (last.startsWith("\"") && last.endsWith("\"")) { | ||||
|           // Dequote the value. We shouldn't have to do this, but | ||||
|           // something is causing some Google Account tokens to get | ||||
|           // wrapped up in double quotes when obtained from the cookie. | ||||
|           // | ||||
|           last = last.substring(1, last.length() - 2); | ||||
|         } | ||||
|         providerId.setText(last); | ||||
|         rememberId.setValue(true); | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     formBody.add(group); | ||||
|   } | ||||
|  | ||||
|   private void link(final String identUrl, final String who, | ||||
|       final ImageResource icon) { | ||||
|     if (!isAllowedProvider(identUrl)) { | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     final ClickHandler i = new ClickHandler() { | ||||
|       @Override | ||||
|       public void onClick(final ClickEvent event) { | ||||
|         event.preventDefault(); | ||||
|         if (!discovering) { | ||||
|           providerId.setText(identUrl); | ||||
|           form.submit(); | ||||
|         } | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     final FlowPanel line = new FlowPanel(); | ||||
|     line.addStyleName(OpenIdResources.I.css().directLink()); | ||||
|  | ||||
|     final Image img = new Image(icon); | ||||
|     img.addClickHandler(i); | ||||
|     line.add(img); | ||||
|  | ||||
|     final Anchor text = new Anchor(); | ||||
|     switch (mode) { | ||||
|       case LINK_IDENTIY: | ||||
|         text.setText(OpenIdUtil.M.linkWith(who)); | ||||
|         break; | ||||
|       case REGISTER: | ||||
|         text.setText(OpenIdUtil.M.registerWith(who)); | ||||
|         break; | ||||
|       case SIGN_IN: | ||||
|       default: | ||||
|         text.setText(OpenIdUtil.M.signInWith(who)); | ||||
|         break; | ||||
|     } | ||||
|     text.setHref(identUrl); | ||||
|     text.addClickHandler(i); | ||||
|     line.add(text); | ||||
|  | ||||
|     formBody.add(line); | ||||
|   } | ||||
|  | ||||
|   private static boolean isAllowedProvider(final String identUrl) { | ||||
|     for (OpenIdProviderPattern p : Gerrit.getConfig().getAllowedOpenIDs()) { | ||||
|       if (p.matches(identUrl)) { | ||||
|         return true; | ||||
|       } | ||||
|     } | ||||
|     return false; | ||||
|   } | ||||
|  | ||||
|   private void enable(final boolean on) { | ||||
|     providerId.setEnabled(on); | ||||
|     login.setEnabled(on); | ||||
|   } | ||||
|  | ||||
|   private void onDiscovery(final DiscoveryResult result) { | ||||
|     discovering = false; | ||||
|  | ||||
|     switch (result.status) { | ||||
|       case VALID: | ||||
|         // The provider won't support operation inside an IFRAME, | ||||
|         // so we replace our entire application. | ||||
|         // | ||||
|         redirectForm.setMethod(FormPanel.METHOD_POST); | ||||
|         redirectForm.setAction(result.providerUrl); | ||||
|         redirectBody.clear(); | ||||
|         for (final Map.Entry<String, String> e : result.providerArgs.entrySet()) { | ||||
|           redirectBody.add(new Hidden(e.getKey(), e.getValue())); | ||||
|         } | ||||
|         FormElement.as(redirectForm.getElement()).setTarget("_top"); | ||||
|         redirectForm.submit(); | ||||
|         break; | ||||
|  | ||||
|       case NOT_ALLOWED: | ||||
|         showError(OpenIdUtil.C.notAllowed()); | ||||
|         enableRetryDiscovery(); | ||||
|         break; | ||||
|  | ||||
|       case NO_PROVIDER: | ||||
|         showError(OpenIdUtil.C.noProvider()); | ||||
|         enableRetryDiscovery(); | ||||
|         break; | ||||
|  | ||||
|       case ERROR: | ||||
|       default: | ||||
|         showError(OpenIdUtil.C.error()); | ||||
|         enableRetryDiscovery(); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private void enableRetryDiscovery() { | ||||
|     enable(true); | ||||
|     providerId.selectAll(); | ||||
|     providerId.setFocus(true); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void onSubmit(final SubmitEvent event) { | ||||
|     event.cancel(); | ||||
|  | ||||
|     String openidIdentifier = providerId.getText(); | ||||
|     if (openidIdentifier == null || openidIdentifier.equals("")) { | ||||
|       enable(true); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     if (!openidIdentifier.startsWith("http://") | ||||
|         && !openidIdentifier.startsWith("https://")) { | ||||
|       openidIdentifier = "http://" + openidIdentifier; | ||||
|     } | ||||
|  | ||||
|     if (!isAllowedProvider(openidIdentifier)) { | ||||
|       showError(OpenIdUtil.C.notAllowed()); | ||||
|       enableRetryDiscovery(); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     discovering = true; | ||||
|     enable(false); | ||||
|     hideError(); | ||||
|  | ||||
|     final boolean remember = rememberId != null && rememberId.getValue(); | ||||
|     OpenIdUtil.SVC.discover(openidIdentifier, mode, remember, token, | ||||
|         new GerritCallback<DiscoveryResult>() { | ||||
|           public void onSuccess(final DiscoveryResult result) { | ||||
|             onDiscovery(result); | ||||
|           } | ||||
|  | ||||
|           @Override | ||||
|           public void onFailure(final Throwable caught) { | ||||
|             super.onFailure(caught); | ||||
|             enableRetryDiscovery(); | ||||
|           } | ||||
|         }); | ||||
|   } | ||||
|  | ||||
|   public void onSubmitComplete(final FormSubmitCompleteEvent event) { | ||||
|   } | ||||
| } | ||||
| @@ -1,72 +0,0 @@ | ||||
| // Copyright (C) 2012 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.client.auth.openid; | ||||
|  | ||||
| import com.google.gerrit.client.Gerrit; | ||||
| import com.google.gerrit.client.rpc.GerritCallback; | ||||
| import com.google.gerrit.common.auth.SignInMode; | ||||
| import com.google.gerrit.common.auth.openid.DiscoveryResult; | ||||
| import com.google.gwt.dom.client.FormElement; | ||||
| import com.google.gwt.user.client.ui.FlowPanel; | ||||
| import com.google.gwt.user.client.ui.FormPanel; | ||||
| import com.google.gwt.user.client.ui.Hidden; | ||||
|  | ||||
| import java.util.Map; | ||||
|  | ||||
| public class OpenIdSsoPanel extends FlowPanel { | ||||
|   private final FormPanel redirectForm; | ||||
|   private final FlowPanel redirectBody; | ||||
|   private final String ssoUrl; | ||||
|  | ||||
|   public OpenIdSsoPanel() { | ||||
|     super(); | ||||
|     redirectBody = new FlowPanel(); | ||||
|     redirectBody.setVisible(false); | ||||
|     redirectForm = new FormPanel(); | ||||
|     redirectForm.add(redirectBody); | ||||
|  | ||||
|     add(redirectForm); | ||||
|  | ||||
|     ssoUrl = Gerrit.getConfig().getOpenIdSsoUrl(); | ||||
|   } | ||||
|  | ||||
|   public void authenticate(SignInMode requestedMode, final String token) { | ||||
|     OpenIdUtil.SVC.discover(ssoUrl, requestedMode, /* remember */ false, token, | ||||
|         new GerritCallback<DiscoveryResult>() { | ||||
|           public void onSuccess(final DiscoveryResult result) { | ||||
|             onDiscovery(result); | ||||
|           } | ||||
|         }); | ||||
|   } | ||||
|  | ||||
|   private void onDiscovery(final DiscoveryResult result) { | ||||
|     switch (result.status) { | ||||
|       case VALID: | ||||
|         redirectForm.setMethod(FormPanel.METHOD_POST); | ||||
|         redirectForm.setAction(result.providerUrl); | ||||
|         redirectBody.clear(); | ||||
|         for (final Map.Entry<String, String> e : result.providerArgs.entrySet()) { | ||||
|           redirectBody.add(new Hidden(e.getKey(), e.getValue())); | ||||
|         } | ||||
|         FormElement.as(redirectForm.getElement()).setTarget("_top"); | ||||
|         redirectForm.submit(); | ||||
|         break; | ||||
|       case ERROR: | ||||
|       case NO_PROVIDER: | ||||
|       case NOT_ALLOWED: | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
| } | ||||
| @@ -14,19 +14,12 @@ | ||||
|  | ||||
| package com.google.gerrit.client.auth.openid; | ||||
|  | ||||
| import com.google.gerrit.common.auth.openid.OpenIdService; | ||||
| import com.google.gwt.core.client.GWT; | ||||
| import com.google.gwtjsonrpc.client.JsonUtil; | ||||
|  | ||||
| public class OpenIdUtil { | ||||
|   public static final OpenIdConstants C; | ||||
|   public static final OpenIdMessages M; | ||||
|   public static final OpenIdService SVC; | ||||
|  | ||||
|   static { | ||||
|     C = GWT.create(OpenIdConstants.class); | ||||
|     M = GWT.create(OpenIdMessages.class); | ||||
|     SVC = GWT.create(OpenIdService.class); | ||||
|     JsonUtil.bind(SVC, "rpc/OpenIdService"); | ||||
|   } | ||||
| } | ||||
|   | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 559 B | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 89 B | 
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 237 B | 
| @@ -1,69 +0,0 @@ | ||||
| /* Copyright (C) 2009 The Android Open Source Project | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| @external .gwt-Button; | ||||
|  | ||||
| @url identifierBackground identifierBackground; | ||||
|  | ||||
| .loginForm { | ||||
|   margin-left: 10px; | ||||
|   margin-right: 10px; | ||||
| } | ||||
|  | ||||
| .logo { | ||||
|   text-align: right; | ||||
| } | ||||
|  | ||||
| .loginLine { | ||||
|   margin-bottom: 10px; | ||||
| } | ||||
| .loginLine div { | ||||
|   white-space: nowrap; | ||||
| } | ||||
| .loginLine .gwt-Button { | ||||
|   margin-left: 2px; | ||||
| } | ||||
|  | ||||
| .identifier { | ||||
|   background: #ffffff identifierBackground no-repeat scroll 5px 50%; | ||||
|   padding-left: 25px; | ||||
|   border: 1px solid #999999; | ||||
| } | ||||
|  | ||||
| .directLink { | ||||
|   vertical-align: middle; | ||||
|   margin-right: 5px; | ||||
|   color: blue; | ||||
|   cursor: pointer; | ||||
| } | ||||
| .directLink:hover { | ||||
|   text-decoration: underline; | ||||
| } | ||||
| .directLink img { | ||||
|   margin-right: 3px; | ||||
|   border: 0 none; | ||||
| } | ||||
|  | ||||
| .error { | ||||
|   padding-top: 5px; | ||||
|   padding-bottom: 5px; | ||||
| } | ||||
| .error span { | ||||
|   padding-top: 4px; | ||||
|   padding-bottom: 4px; | ||||
|   padding-left: 10px; | ||||
|   padding-right: 10px; | ||||
|   background: #fff1a8; | ||||
| } | ||||
										
											Binary file not shown.
										
									
								
							| Before Width: | Height: | Size: 3.4 KiB | 
| @@ -1,26 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.client.auth.userpass; | ||||
|  | ||||
| import com.google.gwt.i18n.client.Constants; | ||||
|  | ||||
| public interface UserPassConstants extends Constants { | ||||
|   String buttonSignIn(); | ||||
|   String username(); | ||||
|   String password(); | ||||
|   String invalidLogin(); | ||||
|   String usernameRequired(); | ||||
|   String passwordRequired(); | ||||
| } | ||||
| @@ -1,6 +0,0 @@ | ||||
| buttonSignIn = Sign In | ||||
| username = Username | ||||
| password = Password | ||||
| invalidLogin = Incorrect username or password. | ||||
| usernameRequired = Please enter a username. | ||||
| passwordRequired = Please enter a password. | ||||
| @@ -1,22 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.client.auth.userpass; | ||||
|  | ||||
| import com.google.gwt.resources.client.CssResource; | ||||
|  | ||||
| public interface UserPassCss extends CssResource { | ||||
|   String loginForm(); | ||||
|   String error(); | ||||
| } | ||||
| @@ -1,23 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.client.auth.userpass; | ||||
|  | ||||
| import com.google.gerrit.reviewdb.client.AuthType; | ||||
| import com.google.gwt.i18n.client.Messages; | ||||
|  | ||||
| public interface UserPassMessages extends Messages { | ||||
|   String signInAt(String hostname); | ||||
|   String authenticationUnavailable(AuthType authType); | ||||
| } | ||||
| @@ -1,2 +0,0 @@ | ||||
| signInAt = Sign In to Gerrit Code Review at {0} | ||||
| authenticationUnavailable = {0} authentication unavailable | ||||
| @@ -1,25 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.client.auth.userpass; | ||||
|  | ||||
| import com.google.gwt.core.client.GWT; | ||||
| import com.google.gwt.resources.client.ClientBundle; | ||||
|  | ||||
| interface UserPassResources extends ClientBundle { | ||||
|   static final UserPassResources I = GWT.create(UserPassResources.class); | ||||
|  | ||||
|   @Source("userpass.css") | ||||
|   UserPassCss css(); | ||||
| } | ||||
| @@ -1,240 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.client.auth.userpass; | ||||
|  | ||||
| import com.google.gerrit.client.Gerrit; | ||||
| import com.google.gerrit.client.SignInDialog; | ||||
| import com.google.gerrit.client.rpc.GerritCallback; | ||||
| import com.google.gerrit.client.ui.SmallHeading; | ||||
| import com.google.gerrit.common.PageLinks; | ||||
| import com.google.gerrit.common.auth.SignInMode; | ||||
| import com.google.gerrit.common.auth.userpass.LoginResult; | ||||
| import com.google.gwt.core.client.Scheduler; | ||||
| import com.google.gwt.core.client.Scheduler.ScheduledCommand; | ||||
| import com.google.gwt.event.dom.client.ClickEvent; | ||||
| import com.google.gwt.event.dom.client.ClickHandler; | ||||
| import com.google.gwt.event.dom.client.KeyCodes; | ||||
| import com.google.gwt.event.dom.client.KeyPressEvent; | ||||
| import com.google.gwt.event.dom.client.KeyPressHandler; | ||||
| import com.google.gwt.user.client.DOM; | ||||
| import com.google.gwt.user.client.Window.Location; | ||||
| import com.google.gwt.user.client.ui.Button; | ||||
| import com.google.gwt.user.client.ui.FlowPanel; | ||||
| import com.google.gwt.user.client.ui.Grid; | ||||
| import com.google.gwt.user.client.ui.InlineLabel; | ||||
| import com.google.gwt.user.client.ui.PasswordTextBox; | ||||
| import com.google.gwt.user.client.ui.TextBox; | ||||
| import com.google.gwtexpui.globalkey.client.GlobalKey; | ||||
| import com.google.gwtexpui.globalkey.client.NpTextBox; | ||||
|  | ||||
| public class UserPassSignInDialog extends SignInDialog { | ||||
|   static { | ||||
|     UserPassResources.I.css().ensureInjected(); | ||||
|   } | ||||
|  | ||||
|   private final FlowPanel formBody; | ||||
|  | ||||
|   private FlowPanel errorLine; | ||||
|   private InlineLabel errorMsg; | ||||
|  | ||||
|   private Button login; | ||||
|   private Button close; | ||||
|   private TextBox username; | ||||
|   private TextBox password; | ||||
|  | ||||
|   public UserPassSignInDialog(final String token, final String initialErrorMsg) { | ||||
|     super(SignInMode.SIGN_IN, token); | ||||
|     setAutoHideEnabled(false); | ||||
|  | ||||
|     formBody = new FlowPanel(); | ||||
|     formBody.setStyleName(UserPassResources.I.css().loginForm()); | ||||
|     add(formBody); | ||||
|  | ||||
|     createHeaderText(); | ||||
|     createErrorBox(); | ||||
|     createUsernameBox(); | ||||
|     if (initialErrorMsg != null) { | ||||
|       showError(initialErrorMsg); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void show() { | ||||
|     super.show(); | ||||
|     Scheduler.get().scheduleDeferred(new ScheduledCommand() { | ||||
|       @Override | ||||
|       public void execute() { | ||||
|         username.setFocus(true); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   private void createHeaderText() { | ||||
|     final FlowPanel headerText = new FlowPanel(); | ||||
|     final SmallHeading headerLabel = new SmallHeading(); | ||||
|     headerLabel.setText(Util.M.signInAt(Location.getHostName())); | ||||
|     headerText.add(headerLabel); | ||||
|     formBody.add(headerText); | ||||
|   } | ||||
|  | ||||
|   private void createErrorBox() { | ||||
|     errorLine = new FlowPanel(); | ||||
|     DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden"); | ||||
|     errorLine.setStyleName(UserPassResources.I.css().error()); | ||||
|  | ||||
|     errorMsg = new InlineLabel(); | ||||
|     errorLine.add(errorMsg); | ||||
|     formBody.add(errorLine); | ||||
|   } | ||||
|  | ||||
|   private void showError(final String msgText) { | ||||
|     errorMsg.setText(msgText); | ||||
|     DOM.setStyleAttribute(errorLine.getElement(), "visibility", ""); | ||||
|   } | ||||
|  | ||||
|   private void hideError() { | ||||
|     DOM.setStyleAttribute(errorLine.getElement(), "visibility", "hidden"); | ||||
|   } | ||||
|  | ||||
|   private void createUsernameBox() { | ||||
|     username = new NpTextBox(); | ||||
|     username.setVisibleLength(25); | ||||
|     username.addKeyPressHandler(new KeyPressHandler() { | ||||
|       @Override | ||||
|       public void onKeyPress(final KeyPressEvent event) { | ||||
|         if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) { | ||||
|           event.preventDefault(); | ||||
|           password.selectAll(); | ||||
|           password.setFocus(true); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     password = new PasswordTextBox(); | ||||
|     password.setVisibleLength(25); | ||||
|     password.addKeyPressHandler(GlobalKey.STOP_PROPAGATION); | ||||
|     password.addKeyPressHandler(new KeyPressHandler() { | ||||
|       @Override | ||||
|       public void onKeyPress(final KeyPressEvent event) { | ||||
|         if (event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ENTER) { | ||||
|           event.preventDefault(); | ||||
|           onLogin(); | ||||
|         } | ||||
|       } | ||||
|     }); | ||||
|  | ||||
|     final FlowPanel buttons = new FlowPanel(); | ||||
|     buttons.setStyleName(Gerrit.RESOURCES.css().errorDialogButtons()); | ||||
|  | ||||
|     login = new Button(); | ||||
|     login.setText(Util.C.buttonSignIn()); | ||||
|     login.addClickHandler(new ClickHandler() { | ||||
|       @Override | ||||
|       public void onClick(final ClickEvent event) { | ||||
|         onLogin(); | ||||
|       } | ||||
|     }); | ||||
|     buttons.add(login); | ||||
|  | ||||
|     close = new Button(); | ||||
|     DOM.setStyleAttribute(close.getElement(), "marginLeft", "45px"); | ||||
|     close.setText(Gerrit.C.signInDialogClose()); | ||||
|     close.addClickHandler(new ClickHandler() { | ||||
|       @Override | ||||
|       public void onClick(ClickEvent event) { | ||||
|         hide(); | ||||
|       } | ||||
|     }); | ||||
|     buttons.add(close); | ||||
|  | ||||
|     final Grid formGrid = new Grid(3, 2); | ||||
|     formGrid.setText(0, 0, Util.C.username()); | ||||
|     formGrid.setText(1, 0, Util.C.password()); | ||||
|     formGrid.setWidget(0, 1, username); | ||||
|     formGrid.setWidget(1, 1, password); | ||||
|     formGrid.setWidget(2, 1, buttons); | ||||
|     formBody.add(formGrid); | ||||
|  | ||||
|     username.setTabIndex(1); | ||||
|     password.setTabIndex(2); | ||||
|     login.setTabIndex(3); | ||||
|     close.setTabIndex(4); | ||||
|   } | ||||
|  | ||||
|   private void enable(final boolean on) { | ||||
|     username.setEnabled(on); | ||||
|     password.setEnabled(on); | ||||
|     login.setEnabled(on); | ||||
|   } | ||||
|  | ||||
|   private void onLogin() { | ||||
|     hideError(); | ||||
|  | ||||
|     final String user = username.getText(); | ||||
|     if (user == null || user.equals("")) { | ||||
|       showError(Util.C.usernameRequired()); | ||||
|       username.setFocus(true); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     final String pass = password.getText(); | ||||
|     if (pass == null || pass.equals("")) { | ||||
|       showError(Util.C.passwordRequired()); | ||||
|       password.setFocus(true); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     enable(false); | ||||
|     Util.SVC.authenticate(user, pass, new GerritCallback<LoginResult>() { | ||||
|       public void onSuccess(final LoginResult result) { | ||||
|         if (result.success) { | ||||
|           String to = token; | ||||
|           if (!to.startsWith("/")) { | ||||
|             to = "/" + to; | ||||
|           } | ||||
|           if (result.isNew && !token.startsWith(PageLinks.REGISTER + "/")) { | ||||
|             to = PageLinks.REGISTER + to; | ||||
|           } | ||||
|           Location.replace(Location.getPath() + "login" + to); | ||||
|         } else { | ||||
|           final String message; | ||||
|           switch (result.getError()) { | ||||
|             case AUTHENTICATION_UNAVAILABLE: | ||||
|               message = Util.M.authenticationUnavailable(result.getAuthType()); | ||||
|               break; | ||||
|             case INVALID_LOGIN: | ||||
|             default: | ||||
|               message = Util.C.invalidLogin(); | ||||
|           } | ||||
|           showError(message); | ||||
|           enable(true); | ||||
|           password.selectAll(); | ||||
|           Scheduler.get().scheduleDeferred(new ScheduledCommand() { | ||||
|             @Override | ||||
|             public void execute() { | ||||
|               password.setFocus(true); | ||||
|             } | ||||
|           }); | ||||
|         } | ||||
|       } | ||||
|  | ||||
|       @Override | ||||
|       public void onFailure(final Throwable caught) { | ||||
|         super.onFailure(caught); | ||||
|         enable(true); | ||||
|       } | ||||
|     }); | ||||
|   } | ||||
| } | ||||
| @@ -1,30 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.client.auth.userpass; | ||||
|  | ||||
| import com.google.gerrit.common.auth.userpass.UserPassAuthService; | ||||
| import com.google.gwt.core.client.GWT; | ||||
| import com.google.gwtjsonrpc.client.JsonUtil; | ||||
|  | ||||
| public class Util { | ||||
|   public static final UserPassConstants C = GWT.create(UserPassConstants.class); | ||||
|   public static final UserPassMessages M = GWT.create(UserPassMessages.class); | ||||
|   public static final UserPassAuthService SVC; | ||||
|  | ||||
|   static { | ||||
|     SVC = GWT.create(UserPassAuthService.class); | ||||
|     JsonUtil.bind(SVC, "rpc/UserPassAuthService"); | ||||
|   } | ||||
| } | ||||
| @@ -1,32 +0,0 @@ | ||||
| /* Copyright (C) 2009 The Android Open Source Project | ||||
|  * | ||||
|  * Licensed under the Apache License, Version 2.0 (the "License"); | ||||
|  * you may not use this file except in compliance with the License. | ||||
|  * You may obtain a copy of the License at | ||||
|  * | ||||
|  * http://www.apache.org/licenses/LICENSE-2.0 | ||||
|  * | ||||
|  * Unless required by applicable law or agreed to in writing, software | ||||
|  * distributed under the License is distributed on an "AS IS" BASIS, | ||||
|  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
|  * See the License for the specific language governing permissions and | ||||
|  * limitations under the License. | ||||
|  */ | ||||
|  | ||||
| .loginForm { | ||||
|   margin-left: 10px; | ||||
|   margin-right: 10px; | ||||
| } | ||||
|  | ||||
| .error { | ||||
|   padding-top: 5px; | ||||
|   padding-bottom: 5px; | ||||
| } | ||||
|  | ||||
| .error span { | ||||
|   padding-top: 4px; | ||||
|   padding-bottom: 4px; | ||||
|   padding-left: 10px; | ||||
|   padding-right: 10px; | ||||
|   background: #fff1a8; | ||||
| } | ||||
| @@ -83,14 +83,6 @@ class GerritConfigProvider implements Provider<GerritConfig> { | ||||
|   private GerritConfig create() throws MalformedURLException { | ||||
|     final GerritConfig config = new GerritConfig(); | ||||
|     switch (authConfig.getAuthType()) { | ||||
|       case OPENID: | ||||
|         config.setAllowedOpenIDs(authConfig.getAllowedOpenIDs()); | ||||
|         break; | ||||
|  | ||||
|       case OPENID_SSO: | ||||
|         config.setOpenIdSsoUrl(authConfig.getOpenIdSsoUrl()); | ||||
|         break; | ||||
|  | ||||
|       case LDAP: | ||||
|       case LDAP_BIND: | ||||
|         config.setRegisterUrl(cfg.getString("auth", null, "registerurl")); | ||||
| @@ -109,6 +101,8 @@ class GerritConfigProvider implements Provider<GerritConfig> { | ||||
|       case DEVELOPMENT_BECOME_ANY_ACCOUNT: | ||||
|       case HTTP: | ||||
|       case HTTP_LDAP: | ||||
|       case OPENID: | ||||
|       case OPENID_SSO: | ||||
|         break; | ||||
|     } | ||||
|     config.setUseContributorAgreements(cfg.getBoolean("auth", | ||||
|   | ||||
| @@ -20,7 +20,7 @@ import static com.google.inject.Scopes.SINGLETON; | ||||
| import com.google.gerrit.common.data.GerritConfig; | ||||
| import com.google.gerrit.extensions.registration.DynamicSet; | ||||
| import com.google.gerrit.extensions.webui.WebUiPlugin; | ||||
| import com.google.gerrit.httpd.auth.become.BecomeAnyAccountLoginServlet; | ||||
| import com.google.gerrit.httpd.auth.become.BecomeAnyAccountModule; | ||||
| import com.google.gerrit.httpd.auth.container.HttpAuthModule; | ||||
| import com.google.gerrit.httpd.auth.container.HttpsClientSslCertModule; | ||||
| import com.google.gerrit.httpd.auth.ldap.LdapAuthModule; | ||||
| @@ -45,7 +45,6 @@ import com.google.inject.Inject; | ||||
| import com.google.inject.Injector; | ||||
| import com.google.inject.ProvisionException; | ||||
| import com.google.inject.servlet.RequestScoped; | ||||
| import com.google.inject.servlet.ServletModule; | ||||
|  | ||||
| import java.net.SocketAddress; | ||||
|  | ||||
| @@ -103,12 +102,7 @@ public class WebModule extends FactoryModule { | ||||
|         break; | ||||
|  | ||||
|       case DEVELOPMENT_BECOME_ANY_ACCOUNT: | ||||
|         install(new ServletModule() { | ||||
|           @Override | ||||
|           protected void configureServlets() { | ||||
|             serve("/become").with(BecomeAnyAccountLoginServlet.class); | ||||
|           } | ||||
|         }); | ||||
|         install(new BecomeAnyAccountModule()); | ||||
|         break; | ||||
|  | ||||
|       case OPENID: | ||||
|   | ||||
| @@ -16,6 +16,8 @@ package com.google.gerrit.httpd.auth.become; | ||||
|  | ||||
| import static com.google.gerrit.reviewdb.client.AccountExternalId.SCHEME_USERNAME; | ||||
|  | ||||
| import com.google.common.base.Objects; | ||||
| import com.google.common.base.Strings; | ||||
| import com.google.gerrit.common.PageLinks; | ||||
| import com.google.gerrit.httpd.HtmlDomUtil; | ||||
| import com.google.gerrit.httpd.WebSession; | ||||
| @@ -52,7 +54,7 @@ import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| @SuppressWarnings("serial") | ||||
| @Singleton | ||||
| public class BecomeAnyAccountLoginServlet extends HttpServlet { | ||||
| class BecomeAnyAccountLoginServlet extends HttpServlet { | ||||
|   private static final boolean IS_DEV = Boolean.getBoolean("Gerrit.GwtDevMode"); | ||||
|  | ||||
|   private final SchemaFactory<ReviewDb> schema; | ||||
| @@ -114,7 +116,9 @@ public class BecomeAnyAccountLoginServlet extends HttpServlet { | ||||
|     if (res != null) { | ||||
|       webSession.get().login(res, false); | ||||
|       final StringBuilder rdr = new StringBuilder(); | ||||
|       rdr.append(req.getContextPath()); | ||||
|       rdr.append(Objects.firstNonNull( | ||||
|           Strings.emptyToNull(req.getContextPath()), | ||||
|           "/")); | ||||
|       if (IS_DEV && req.getParameter("gwt.codesvr") != null) { | ||||
|         if (rdr.indexOf("?") < 0) { | ||||
|           rdr.append("?"); | ||||
|   | ||||
| @@ -1,4 +1,4 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // Copyright (C) 2013 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| @@ -12,15 +12,13 @@ | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
| 
 | ||||
| package com.google.gerrit.client.auth.openid; | ||||
| package com.google.gerrit.httpd.auth.become; | ||||
| 
 | ||||
| import com.google.gwt.resources.client.CssResource; | ||||
| import com.google.inject.servlet.ServletModule; | ||||
| 
 | ||||
| interface OpenIdCss extends CssResource { | ||||
|   String loginForm(); | ||||
|   String logo(); | ||||
|   String loginLine(); | ||||
|   String identifier(); | ||||
|   String directLink(); | ||||
|   String error(); | ||||
| public class BecomeAnyAccountModule extends ServletModule { | ||||
|   @Override | ||||
|   protected void configureServlets() { | ||||
|     serve("/login/*").with(BecomeAnyAccountLoginServlet.class); | ||||
|   } | ||||
| } | ||||
| @@ -14,20 +14,12 @@ | ||||
|  | ||||
| package com.google.gerrit.httpd.auth.ldap; | ||||
|  | ||||
| import com.google.gerrit.httpd.rpc.RpcServletModule; | ||||
| import com.google.gerrit.httpd.rpc.UiRpcModule; | ||||
| import com.google.inject.servlet.ServletModule; | ||||
|  | ||||
| /** RPC support related to username/password LDAP authentication. */ | ||||
| /** Configure username/password LDAP authentication. */ | ||||
| public class LdapAuthModule extends ServletModule { | ||||
|   @Override | ||||
|   protected void configureServlets() { | ||||
|     serve("/login/*").with(LoginRedirectServlet.class); | ||||
|     install(new RpcServletModule(UiRpcModule.PREFIX) { | ||||
|       @Override | ||||
|       protected void configureServlets() { | ||||
|         rpc(UserPassAuthServiceImpl.class); | ||||
|       } | ||||
|     }); | ||||
|     serve("/login/*").with(LdapLoginServlet.class); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -0,0 +1,166 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.httpd.auth.ldap; | ||||
|  | ||||
| import com.google.common.base.Objects; | ||||
| import com.google.common.base.Strings; | ||||
| import com.google.gerrit.common.PageLinks; | ||||
| import com.google.gerrit.httpd.HtmlDomUtil; | ||||
| import com.google.gerrit.httpd.WebSession; | ||||
| import com.google.gerrit.server.account.AccountException; | ||||
| import com.google.gerrit.server.account.AccountManager; | ||||
| import com.google.gerrit.server.account.AccountUserNameException; | ||||
| import com.google.gerrit.server.account.AuthRequest; | ||||
| import com.google.gerrit.server.account.AuthResult; | ||||
| import com.google.gerrit.server.auth.AuthenticationUnavailableException; | ||||
| import com.google.gerrit.server.config.CanonicalWebUrl; | ||||
| import com.google.gwtexpui.server.CacheHeaders; | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Provider; | ||||
| import com.google.inject.Singleton; | ||||
|  | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.w3c.dom.Document; | ||||
| import org.w3c.dom.Element; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| import javax.annotation.Nullable; | ||||
| import javax.servlet.ServletException; | ||||
| import javax.servlet.ServletOutputStream; | ||||
| import javax.servlet.http.HttpServlet; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| /** Handles username/password based authentication against the directory. */ | ||||
| @SuppressWarnings("serial") | ||||
| @Singleton | ||||
| class LdapLoginServlet extends HttpServlet { | ||||
|   private static final Logger log = LoggerFactory | ||||
|       .getLogger(LdapLoginServlet.class); | ||||
|  | ||||
|   private final AccountManager accountManager; | ||||
|   private final Provider<WebSession> webSession; | ||||
|   private final Provider<String> urlProvider; | ||||
|  | ||||
|   @Inject | ||||
|   LdapLoginServlet(AccountManager accountManager, | ||||
|       Provider<WebSession> webSession, | ||||
|       @CanonicalWebUrl @Nullable Provider<String> urlProvider) { | ||||
|     this.accountManager = accountManager; | ||||
|     this.webSession = webSession; | ||||
|     this.urlProvider = urlProvider; | ||||
|  | ||||
|     if (Strings.isNullOrEmpty(urlProvider.get())) { | ||||
|       log.error("gerrit.canonicalWebUrl must be set in gerrit.config"); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private void sendForm(HttpServletRequest req, HttpServletResponse res, | ||||
|       @Nullable String errorMessage) throws IOException { | ||||
|     String self = req.getRequestURI(); | ||||
|     String cancel = Objects.firstNonNull(urlProvider.get(), "/"); | ||||
|     String token = getToken(req); | ||||
|     if (!token.equals("/")) { | ||||
|       cancel += "#" + token; | ||||
|     } | ||||
|  | ||||
|     Document doc = | ||||
|         HtmlDomUtil.parseFile(LdapLoginServlet.class, "LoginForm.html"); | ||||
|     HtmlDomUtil.find(doc, "hostName").setTextContent(req.getServerName()); | ||||
|     HtmlDomUtil.find(doc, "login_form").setAttribute("action", self); | ||||
|     HtmlDomUtil.find(doc, "cancel_link").setAttribute("href", cancel); | ||||
|  | ||||
|     Element emsg = HtmlDomUtil.find(doc, "error_message"); | ||||
|     if (Strings.isNullOrEmpty(errorMessage)) { | ||||
|       emsg.getParentNode().removeChild(emsg); | ||||
|     } else { | ||||
|       emsg.setTextContent(errorMessage); | ||||
|     } | ||||
|  | ||||
|     byte[] bin = HtmlDomUtil.toUTF8(doc); | ||||
|     res.setStatus(HttpServletResponse.SC_UNAUTHORIZED); | ||||
|     res.setContentType("text/html"); | ||||
|     res.setCharacterEncoding("UTF-8"); | ||||
|     res.setContentLength(bin.length); | ||||
|     ServletOutputStream out = res.getOutputStream(); | ||||
|     try { | ||||
|       out.write(bin); | ||||
|     } finally { | ||||
|       out.close(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   protected void doGet(HttpServletRequest req, HttpServletResponse res) | ||||
|       throws IOException { | ||||
|     sendForm(req, res, null); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   protected void doPost(HttpServletRequest req, HttpServletResponse res) | ||||
|       throws ServletException, IOException { | ||||
|     String username = Strings.nullToEmpty(req.getParameter("username")).trim(); | ||||
|     String password = Strings.nullToEmpty(req.getParameter("password")); | ||||
|     String remember = Strings.nullToEmpty(req.getParameter("rememberme")); | ||||
|     if (username.isEmpty() || password.isEmpty()) { | ||||
|       sendForm(req, res, "Invalid username or password."); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     AuthRequest areq = AuthRequest.forUser(username); | ||||
|     areq.setPassword(password); | ||||
|  | ||||
|     AuthResult ares; | ||||
|     try { | ||||
|       ares = accountManager.authenticate(areq); | ||||
|     } catch (AccountUserNameException e) { | ||||
|       sendForm(req, res, e.getMessage()); | ||||
|       return; | ||||
|     } catch (AuthenticationUnavailableException e) { | ||||
|       sendForm(req, res, "Authentication unavailable at this time."); | ||||
|       return; | ||||
|     } catch (AccountException e) { | ||||
|       log.info(String.format("'%s' failed to sign in: %s", username, e.getMessage())); | ||||
|       sendForm(req, res, "Invalid username or password."); | ||||
|       return; | ||||
|     } catch (RuntimeException e) { | ||||
|       log.error("LDAP authentication failed", e); | ||||
|       sendForm(req, res, "Authentication unavailable at this time."); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     String token = getToken(req); | ||||
|     StringBuilder dest = new StringBuilder(); | ||||
|     dest.append(urlProvider.get()); | ||||
|     dest.append('#'); | ||||
|     dest.append(token); | ||||
|  | ||||
|     CacheHeaders.setNotCacheable(res); | ||||
|     webSession.get().login(ares, "1".equals(remember)); | ||||
|     res.sendRedirect(dest.toString()); | ||||
|   } | ||||
|  | ||||
|   private static String getToken(final HttpServletRequest req) { | ||||
|     String token = req.getPathInfo(); | ||||
|     if (token == null || token.isEmpty()) { | ||||
|       token = PageLinks.MINE; | ||||
|     } else if (!token.startsWith("/")) { | ||||
|       token = "/" + token; | ||||
|     } | ||||
|     return token; | ||||
|   } | ||||
| } | ||||
| @@ -1,84 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.httpd.auth.ldap; | ||||
|  | ||||
| import com.google.gerrit.common.PageLinks; | ||||
| import com.google.gerrit.common.auth.SignInMode; | ||||
| import com.google.gerrit.httpd.WebSession; | ||||
| import com.google.gerrit.server.config.CanonicalWebUrl; | ||||
| import com.google.gwtexpui.server.CacheHeaders; | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Provider; | ||||
| import com.google.inject.Singleton; | ||||
|  | ||||
| import java.io.IOException; | ||||
|  | ||||
| import javax.annotation.Nullable; | ||||
| import javax.servlet.http.HttpServlet; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| /** | ||||
|  * Servlet bound to {@code /login/*} to redirect after user/pass sign-in. | ||||
|  * <p> | ||||
|  * This servlet is required because user authentication is done over RPC, but if | ||||
|  * the RPC is successful we need to force the host page to fully reload to pick | ||||
|  * up the account information, as we don't support updating the UI on the fly | ||||
|  * after a sign-in. | ||||
|  */ | ||||
| @Singleton | ||||
| class LoginRedirectServlet extends HttpServlet { | ||||
|   private static final long serialVersionUID = 1L; | ||||
|  | ||||
|   private final Provider<WebSession> webSession; | ||||
|   private final Provider<String> urlProvider; | ||||
|  | ||||
|   @Inject | ||||
|   LoginRedirectServlet(final Provider<WebSession> webSession, | ||||
|       @CanonicalWebUrl @Nullable final Provider<String> urlProvider) { | ||||
|     this.webSession = webSession; | ||||
|     this.urlProvider = urlProvider; | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   protected void doGet(final HttpServletRequest req, | ||||
|       final HttpServletResponse rsp) throws IOException { | ||||
|     final String token; | ||||
|     if (webSession.get().isSignedIn()) { | ||||
|       token = getToken(req); | ||||
|     } else { | ||||
|       final String msg = "Session cookie not available."; | ||||
|       token = "/SignInFailure," + SignInMode.SIGN_IN + "," + msg; | ||||
|     } | ||||
|  | ||||
|     final StringBuilder rdr = new StringBuilder(); | ||||
|     rdr.append(urlProvider.get()); | ||||
|     rdr.append('#'); | ||||
|     rdr.append(token); | ||||
|  | ||||
|     CacheHeaders.setNotCacheable(rsp); | ||||
|     rsp.sendRedirect(rdr.toString()); | ||||
|   } | ||||
|  | ||||
|   private String getToken(final HttpServletRequest req) { | ||||
|     String token = req.getPathInfo(); | ||||
|     if (token == null || token.isEmpty()) { | ||||
|       token = PageLinks.MINE; | ||||
|     } else if (!token.startsWith("/")) { | ||||
|       token = "/" + token; | ||||
|     } | ||||
|     return token; | ||||
|   } | ||||
| } | ||||
| @@ -1,92 +0,0 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.httpd.auth.ldap; | ||||
|  | ||||
| import com.google.gerrit.common.auth.userpass.LoginResult; | ||||
| import com.google.gerrit.common.auth.userpass.UserPassAuthService; | ||||
| import com.google.gerrit.httpd.WebSession; | ||||
| import com.google.gerrit.reviewdb.client.AuthType; | ||||
| import com.google.gerrit.server.account.AccountException; | ||||
| import com.google.gerrit.server.account.AccountManager; | ||||
| import com.google.gerrit.server.account.AccountUserNameException; | ||||
| import com.google.gerrit.server.account.AuthRequest; | ||||
| import com.google.gerrit.server.account.AuthResult; | ||||
| import com.google.gerrit.server.auth.AuthenticationUnavailableException; | ||||
| import com.google.gerrit.server.config.AuthConfig; | ||||
| import com.google.gwtjsonrpc.common.AsyncCallback; | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Provider; | ||||
|  | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
|  | ||||
| class UserPassAuthServiceImpl implements UserPassAuthService { | ||||
|   private final Provider<WebSession> webSession; | ||||
|   private final AccountManager accountManager; | ||||
|   private final AuthType authType; | ||||
|  | ||||
|   private static final Logger log = LoggerFactory | ||||
|       .getLogger(UserPassAuthServiceImpl.class); | ||||
|  | ||||
|   @Inject | ||||
|   UserPassAuthServiceImpl(final Provider<WebSession> webSession, | ||||
|       final AccountManager accountManager, final AuthConfig authConfig) { | ||||
|     this.webSession = webSession; | ||||
|     this.accountManager = accountManager; | ||||
|     this.authType = authConfig.getAuthType(); | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   public void authenticate(String username, final String password, | ||||
|       final AsyncCallback<LoginResult> callback) { | ||||
|     LoginResult result = new LoginResult(authType); | ||||
|     if (username == null || "".equals(username.trim()) // | ||||
|         || password == null || "".equals(password)) { | ||||
|       result.setError(LoginResult.Error.INVALID_LOGIN); | ||||
|       callback.onSuccess(result); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     username = username.trim(); | ||||
|  | ||||
|     final AuthRequest req = AuthRequest.forUser(username); | ||||
|     req.setPassword(password); | ||||
|  | ||||
|     final AuthResult res; | ||||
|     try { | ||||
|       res = accountManager.authenticate(req); | ||||
|     } catch (AccountUserNameException e) { | ||||
|       // entered user name and password were correct, but user name could not be | ||||
|       // set for the newly created account and this is why the login fails, | ||||
|       // error screen with error message should be shown to the user | ||||
|       callback.onFailure(e); | ||||
|       return; | ||||
|     } catch (AuthenticationUnavailableException e) { | ||||
|       result.setError(LoginResult.Error.AUTHENTICATION_UNAVAILABLE); | ||||
|       callback.onSuccess(result); | ||||
|       return; | ||||
|     } catch (AccountException e) { | ||||
|       log.info(String.format("'%s' failed to sign in: %s", username, e.getMessage())); | ||||
|       result.setError(LoginResult.Error.INVALID_LOGIN); | ||||
|       callback.onSuccess(result); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     result.success = true; | ||||
|     result.isNew = res.isNew(); | ||||
|     webSession.get().login(res, true /* persistent cookie */); | ||||
|     callback.onSuccess(result); | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,75 @@ | ||||
| <html> | ||||
|   <head> | ||||
|     <title>Gerrit Code Review - Sign In</title> | ||||
|     <style> | ||||
|       #error_message { | ||||
|         padding: 5px; | ||||
|         margin: 2em; | ||||
|         width: 20em; | ||||
|         background-color: rgb(255, 255, 116); | ||||
|         font-weight: bold; | ||||
|       } | ||||
|       #cancel_link { | ||||
|         margin-left: 45px; | ||||
|       } | ||||
|     </style> | ||||
|   </head> | ||||
|   <body> | ||||
|     <h1>Sign In to Gerrit Code Review at <span id="hostName">example.com</span></h1> | ||||
|     <div id="error_message">Invalid username or password.</div> | ||||
|     <form method="POST" action="#" id="login_form"> | ||||
|       <table style="border: 0;"> | ||||
|       <tr> | ||||
|         <th>Username</th> | ||||
|         <td><input name="username" id="f_user" | ||||
|                    type="text" | ||||
|                    size="25" | ||||
|                    tabindex="1" /></td> | ||||
|       </tr> | ||||
|       <tr> | ||||
|         <th>Password</th> | ||||
|         <td><input name="password" id="f_pass" | ||||
|                    type="password" | ||||
|                    size="25" | ||||
|                    tabindex="2" /></td> | ||||
|       </tr> | ||||
|       <tr> | ||||
|         <td></td> | ||||
|         <td> | ||||
|           <input name="rememberme" id="f_remember" | ||||
|                  type="checkbox" | ||||
|                  value="1" | ||||
|                  tabindex="3" /> | ||||
|           <label for="f_remember">Remember me</label> | ||||
|         </td> | ||||
|       </tr> | ||||
|       <tr> | ||||
|         <td></td> | ||||
|         <td> | ||||
|           <input type="submit" value="Sign In" tabindex="4"/> | ||||
|           <a href="../" id="cancel_link">Cancel</a> | ||||
|         </td> | ||||
|       </tr> | ||||
|       </table> | ||||
|     </form> | ||||
|  | ||||
|     <script type="text/javascript" language="javascript"> | ||||
|       var login_form = document.getElementById('login_form'); | ||||
|       var f_user = document.getElementById('f_user'); | ||||
|       var f_pass = document.getElementById('f_pass'); | ||||
|       f_user.onkeydown = function(e) { | ||||
|         if (e.keyCode == 13) { | ||||
|           f_pass.focus(); | ||||
|           return false; | ||||
|         } | ||||
|       } | ||||
|       f_pass.onkeydown = function(e) { | ||||
|         if (e.keyCode == 13) { | ||||
|           login_form.submit(); | ||||
|           return false; | ||||
|         } | ||||
|       } | ||||
|       f_user.focus(); | ||||
|     </script> | ||||
|   </body> | ||||
| </html> | ||||
| @@ -12,18 +12,15 @@ | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
| 
 | ||||
| package com.google.gerrit.common.auth.openid; | ||||
| package com.google.gerrit.httpd.auth.openid; | ||||
| 
 | ||||
| import java.util.Map; | ||||
| 
 | ||||
| public final class DiscoveryResult { | ||||
|   public static enum Status { | ||||
| final class DiscoveryResult { | ||||
|   static enum Status { | ||||
|     /** Provider was discovered and {@code providerUrl} is valid. */ | ||||
|     VALID, | ||||
| 
 | ||||
|     /** The identifier is not allowed to be used, by site configuration. */ | ||||
|     NOT_ALLOWED, | ||||
| 
 | ||||
|     /** Identifier isn't for an OpenID provider. */ | ||||
|     NO_PROVIDER, | ||||
| 
 | ||||
| @@ -31,20 +28,20 @@ public final class DiscoveryResult { | ||||
|     ERROR; | ||||
|   } | ||||
| 
 | ||||
|   public Status status; | ||||
|   public String providerUrl; | ||||
|   public Map<String, String> providerArgs; | ||||
|   Status status; | ||||
|   String providerUrl; | ||||
|   Map<String, String> providerArgs; | ||||
| 
 | ||||
|   protected DiscoveryResult() { | ||||
|   DiscoveryResult() { | ||||
|   } | ||||
| 
 | ||||
|   public DiscoveryResult(final String redirect, final Map<String, String> args) { | ||||
|   DiscoveryResult(String redirect, Map<String, String> args) { | ||||
|     status = Status.VALID; | ||||
|     providerUrl = redirect; | ||||
|     providerArgs = args; | ||||
|   } | ||||
| 
 | ||||
|   public DiscoveryResult(final Status s) { | ||||
|   DiscoveryResult(Status s) { | ||||
|     status = s; | ||||
|   } | ||||
| } | ||||
| @@ -0,0 +1,306 @@ | ||||
| // Copyright (C) 2009 The Android Open Source Project | ||||
| // | ||||
| // Licensed under the Apache License, Version 2.0 (the "License"); | ||||
| // you may not use this file except in compliance with the License. | ||||
| // You may obtain a copy of the License at | ||||
| // | ||||
| // http://www.apache.org/licenses/LICENSE-2.0 | ||||
| // | ||||
| // Unless required by applicable law or agreed to in writing, software | ||||
| // distributed under the License is distributed on an "AS IS" BASIS, | ||||
| // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
|  | ||||
| package com.google.gerrit.httpd.auth.openid; | ||||
|  | ||||
| import com.google.common.base.Objects; | ||||
| import com.google.common.base.Strings; | ||||
| import com.google.common.collect.ImmutableMap; | ||||
| import com.google.common.collect.ImmutableSet; | ||||
| import com.google.common.collect.Sets; | ||||
| import com.google.gerrit.common.PageLinks; | ||||
| import com.google.gerrit.common.auth.openid.OpenIdUrls; | ||||
| import com.google.gerrit.extensions.restapi.Url; | ||||
| import com.google.gerrit.httpd.HtmlDomUtil; | ||||
| import com.google.gerrit.reviewdb.client.AuthType; | ||||
| import com.google.gerrit.server.config.AuthConfig; | ||||
| import com.google.gerrit.server.config.CanonicalWebUrl; | ||||
| import com.google.gerrit.server.config.GerritServerConfig; | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Provider; | ||||
| import com.google.inject.Singleton; | ||||
|  | ||||
| import org.eclipse.jgit.lib.Config; | ||||
| import org.slf4j.Logger; | ||||
| import org.slf4j.LoggerFactory; | ||||
| import org.w3c.dom.Document; | ||||
| import org.w3c.dom.Element; | ||||
|  | ||||
| import java.io.IOException; | ||||
| import java.util.Map; | ||||
| import java.util.Set; | ||||
|  | ||||
| import javax.annotation.Nullable; | ||||
| import javax.servlet.ServletOutputStream; | ||||
| import javax.servlet.http.Cookie; | ||||
| import javax.servlet.http.HttpServlet; | ||||
| import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| /** Handles OpenID based login flow. */ | ||||
| @SuppressWarnings("serial") | ||||
| @Singleton | ||||
| class LoginForm extends HttpServlet { | ||||
|   private static final Logger log = LoggerFactory.getLogger(LoginForm.class); | ||||
|   private static final ImmutableMap<String, String> ALL_PROVIDERS = ImmutableMap.of( | ||||
|       "google", OpenIdUrls.URL_GOOGLE, | ||||
|       "yahoo", OpenIdUrls.URL_YAHOO); | ||||
|  | ||||
|   private final ImmutableSet<String> suggestProviders; | ||||
|   private final Provider<String> urlProvider; | ||||
|   private final OpenIdServiceImpl impl; | ||||
|   private final int maxRedirectUrlLength; | ||||
|   private final String ssoUrl; | ||||
|  | ||||
|   @Inject | ||||
|   LoginForm( | ||||
|       @CanonicalWebUrl @Nullable Provider<String> urlProvider, | ||||
|       @GerritServerConfig Config config, | ||||
|       AuthConfig authConfig, | ||||
|       OpenIdServiceImpl impl) { | ||||
|     this.urlProvider = urlProvider; | ||||
|     this.impl = impl; | ||||
|     this.maxRedirectUrlLength = config.getInt( | ||||
|         "openid", "maxRedirectUrlLength", | ||||
|         10); | ||||
|  | ||||
|     if (Strings.isNullOrEmpty(urlProvider.get())) { | ||||
|       log.error("gerrit.canonicalWebUrl must be set in gerrit.config"); | ||||
|     } | ||||
|  | ||||
|     if (authConfig.getAuthType() == AuthType.OPENID_SSO) { | ||||
|       suggestProviders = ImmutableSet.of(); | ||||
|       ssoUrl = authConfig.getOpenIdSsoUrl(); | ||||
|     } else { | ||||
|       Set<String> providers = Sets.newHashSet(); | ||||
|       for (Map.Entry<String, String> e : ALL_PROVIDERS.entrySet()) { | ||||
|         if (impl.isAllowedOpenID(e.getValue())) { | ||||
|           providers.add(e.getKey()); | ||||
|         } | ||||
|       } | ||||
|       suggestProviders = ImmutableSet.copyOf(providers); | ||||
|       ssoUrl = null; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   protected void doGet(HttpServletRequest req, HttpServletResponse res) | ||||
|       throws IOException { | ||||
|     if (ssoUrl != null) { | ||||
|       String token = getToken(req); | ||||
|       SignInMode mode; | ||||
|       if (PageLinks.REGISTER.equals(token)) { | ||||
|         mode = SignInMode.REGISTER; | ||||
|         token = PageLinks.MINE; | ||||
|       } else { | ||||
|         mode = SignInMode.SIGN_IN; | ||||
|       } | ||||
|       discover(req, res, false, ssoUrl, false, token, mode); | ||||
|     } else { | ||||
|       String id = Strings.nullToEmpty(req.getParameter("id")).trim(); | ||||
|       if (!id.isEmpty()) { | ||||
|         doPost(req, res); | ||||
|       } else { | ||||
|         boolean link = req.getParameter("link") != null; | ||||
|         sendForm(req, res, link, null); | ||||
|       } | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   @Override | ||||
|   protected void doPost(HttpServletRequest req, HttpServletResponse res) | ||||
|       throws IOException { | ||||
|     boolean link = req.getParameter("link") != null; | ||||
|     String id = Strings.nullToEmpty(req.getParameter("id")).trim(); | ||||
|     if (id.isEmpty()) { | ||||
|       sendForm(req, res, link, null); | ||||
|       return; | ||||
|     } | ||||
|     if (!id.startsWith("http://") && !id.startsWith("https://")) { | ||||
|       id = "http://" + id; | ||||
|     } | ||||
|     if ((ssoUrl != null && !ssoUrl.equals(id)) || !impl.isAllowedOpenID(id)) { | ||||
|       sendForm(req, res, link, "OpenID provider not permitted by site policy."); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     boolean remember = "1".equals(req.getParameter("rememberme")); | ||||
|     String token = getToken(req); | ||||
|     SignInMode mode; | ||||
|     if (link) { | ||||
|       mode = SignInMode.LINK_IDENTIY; | ||||
|     } else if (PageLinks.REGISTER.equals(token)) { | ||||
|       mode = SignInMode.REGISTER; | ||||
|       token = PageLinks.MINE; | ||||
|     } else { | ||||
|       mode = SignInMode.SIGN_IN; | ||||
|     } | ||||
|  | ||||
|     discover(req, res, link, id, remember, token, mode); | ||||
|   } | ||||
|  | ||||
|   private void discover(HttpServletRequest req, HttpServletResponse res, | ||||
|       boolean link, String id, boolean remember, String token, SignInMode mode) | ||||
|       throws IOException { | ||||
|     if (ssoUrl != null) { | ||||
|       remember = false; | ||||
|     } | ||||
|  | ||||
|     DiscoveryResult r = impl.discover(id, mode, remember, token); | ||||
|     switch (r.status) { | ||||
|       case VALID: | ||||
|         redirect(r, res); | ||||
|         break; | ||||
|  | ||||
|       case NO_PROVIDER: | ||||
|         sendForm(req, res, link, | ||||
|             "Provider is not supported, or was incorrectly entered."); | ||||
|         break; | ||||
|  | ||||
|       case ERROR: | ||||
|         sendForm(req, res, link, "Unable to connect with OpenID provider."); | ||||
|         break; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private void redirect(DiscoveryResult r, HttpServletResponse res) | ||||
|       throws IOException { | ||||
|     StringBuilder url = new StringBuilder(); | ||||
|     url.append(r.providerUrl); | ||||
|     if (r.providerArgs != null && !r.providerArgs.isEmpty()) { | ||||
|       boolean first = true; | ||||
|       for(Map.Entry<String, String> arg : r.providerArgs.entrySet()) { | ||||
|         if (first) { | ||||
|           url.append('?'); | ||||
|           first = false; | ||||
|         } else { | ||||
|           url.append('&'); | ||||
|         } | ||||
|         url.append(Url.encode(arg.getKey())) | ||||
|            .append('=') | ||||
|            .append(Url.encode(arg.getValue())); | ||||
|       } | ||||
|     } | ||||
|     if (url.length() <= maxRedirectUrlLength) { | ||||
|       res.sendRedirect(url.toString()); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|     Document doc = HtmlDomUtil.parseFile(LoginForm.class, "RedirectForm.html"); | ||||
|     Element form = HtmlDomUtil.find(doc, "redirect_form"); | ||||
|     form.setAttribute("action", r.providerUrl); | ||||
|     if (r.providerArgs != null && !r.providerArgs.isEmpty()) { | ||||
|       for (Map.Entry<String, String> arg : r.providerArgs.entrySet()) { | ||||
|         Element in = doc.createElement("input"); | ||||
|         in.setAttribute("type", "hidden"); | ||||
|         in.setAttribute("name", arg.getKey()); | ||||
|         in.setAttribute("value", arg.getValue()); | ||||
|         form.appendChild(in); | ||||
|       } | ||||
|     } | ||||
|     sendHtml(res, doc); | ||||
|   } | ||||
|  | ||||
|   private static String getToken(HttpServletRequest req) { | ||||
|     String token = req.getPathInfo(); | ||||
|     if (token == null || token.isEmpty()) { | ||||
|       token = PageLinks.MINE; | ||||
|     } else if (!token.startsWith("/")) { | ||||
|       token = "/" + token; | ||||
|     } | ||||
|     return token; | ||||
|   } | ||||
|  | ||||
|   private void sendForm(HttpServletRequest req, HttpServletResponse res, | ||||
|       boolean link, @Nullable String errorMessage) throws IOException { | ||||
|     String self = req.getRequestURI(); | ||||
|     String cancel = Objects.firstNonNull(urlProvider.get(), "/"); | ||||
|     String token = getToken(req); | ||||
|     if (!token.equals("/")) { | ||||
|       cancel += "#" + token; | ||||
|     } | ||||
|  | ||||
|     Document doc = HtmlDomUtil.parseFile(LoginForm.class, "LoginForm.html"); | ||||
|     HtmlDomUtil.find(doc, "hostName").setTextContent(req.getServerName()); | ||||
|     HtmlDomUtil.find(doc, "login_form").setAttribute("action", self); | ||||
|     HtmlDomUtil.find(doc, "cancel_link").setAttribute("href", cancel); | ||||
|  | ||||
|     if (!link || ssoUrl != null) { | ||||
|       Element input = HtmlDomUtil.find(doc, "f_link"); | ||||
|       input.getParentNode().removeChild(input); | ||||
|     } | ||||
|  | ||||
|     String last = getLastId(req); | ||||
|     if (last != null) { | ||||
|       HtmlDomUtil.find(doc, "f_openid").setAttribute("value", last); | ||||
|     } | ||||
|  | ||||
|     Element emsg = HtmlDomUtil.find(doc, "error_message"); | ||||
|     if (Strings.isNullOrEmpty(errorMessage)) { | ||||
|       emsg.getParentNode().removeChild(emsg); | ||||
|     } else { | ||||
|       emsg.setTextContent(errorMessage); | ||||
|     } | ||||
|  | ||||
|     for (String name : ALL_PROVIDERS.keySet()) { | ||||
|       Element div = HtmlDomUtil.find(doc, "provider_" + name); | ||||
|       if (div == null) { | ||||
|         continue; | ||||
|       } | ||||
|       if (!suggestProviders.contains(name)) { | ||||
|         div.getParentNode().removeChild(div); | ||||
|         continue; | ||||
|       } | ||||
|       Element a = HtmlDomUtil.find(div, "id_" + name); | ||||
|       if (a == null) { | ||||
|         div.getParentNode().removeChild(div); | ||||
|         continue; | ||||
|       } | ||||
|       StringBuilder u = new StringBuilder(); | ||||
|       u.append(self).append(a.getAttribute("href")); | ||||
|       if (link) { | ||||
|         u.append("&link"); | ||||
|       } | ||||
|       a.setAttribute("href", u.toString()); | ||||
|     } | ||||
|     sendHtml(res, doc); | ||||
|   } | ||||
|  | ||||
|   private void sendHtml(HttpServletResponse res, Document doc) | ||||
|       throws IOException { | ||||
|     byte[] bin = HtmlDomUtil.toUTF8(doc); | ||||
|     res.setStatus(HttpServletResponse.SC_UNAUTHORIZED); | ||||
|     res.setContentType("text/html"); | ||||
|     res.setCharacterEncoding("UTF-8"); | ||||
|     res.setContentLength(bin.length); | ||||
|     ServletOutputStream out = res.getOutputStream(); | ||||
|     try { | ||||
|       out.write(bin); | ||||
|     } finally { | ||||
|       out.close(); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   private static String getLastId(HttpServletRequest req) { | ||||
|     Cookie[] cookies = req.getCookies(); | ||||
|     if (cookies != null) { | ||||
|       for (Cookie c : cookies) { | ||||
|         if (OpenIdUrls.LASTID_COOKIE.equals(c.getName())) { | ||||
|           return c.getValue(); | ||||
|         } | ||||
|       } | ||||
|     } | ||||
|     return null; | ||||
|   } | ||||
| } | ||||
| @@ -14,22 +14,16 @@ | ||||
|  | ||||
| package com.google.gerrit.httpd.auth.openid; | ||||
|  | ||||
| import com.google.gerrit.httpd.rpc.RpcServletModule; | ||||
| import com.google.inject.servlet.ServletModule; | ||||
|  | ||||
| /** Servlets and RPC support related to OpenID authentication. */ | ||||
| /** Servlets related to OpenID authentication. */ | ||||
| public class OpenIdModule extends ServletModule { | ||||
|   @Override | ||||
|   protected void configureServlets() { | ||||
|     serve("/login/*").with(LoginForm.class); | ||||
|     serve("/" + OpenIdServiceImpl.RETURN_URL).with(OpenIdLoginServlet.class); | ||||
|     serve("/" + XrdsServlet.LOCATION).with(XrdsServlet.class); | ||||
|     filter("/").through(XrdsFilter.class); | ||||
|  | ||||
|     install(new RpcServletModule(RpcServletModule.PREFIX) { | ||||
|       @Override | ||||
|       protected void configureServlets() { | ||||
|         rpc(OpenIdServiceImpl.class); | ||||
|       } | ||||
|     }); | ||||
|     bind(OpenIdServiceImpl.class); | ||||
|   } | ||||
| } | ||||
|   | ||||
| @@ -15,10 +15,6 @@ | ||||
| package com.google.gerrit.httpd.auth.openid; | ||||
|  | ||||
| import com.google.gerrit.common.PageLinks; | ||||
| import com.google.gerrit.common.auth.SignInMode; | ||||
| import com.google.gerrit.common.auth.openid.DiscoveryResult; | ||||
| import com.google.gerrit.common.auth.openid.OpenIdProviderPattern; | ||||
| import com.google.gerrit.common.auth.openid.OpenIdService; | ||||
| import com.google.gerrit.common.auth.openid.OpenIdUrls; | ||||
| import com.google.gerrit.httpd.WebSession; | ||||
| import com.google.gerrit.reviewdb.client.Account; | ||||
| @@ -26,11 +22,11 @@ import com.google.gerrit.server.IdentifiedUser; | ||||
| import com.google.gerrit.server.UrlEncoded; | ||||
| import com.google.gerrit.server.account.AccountException; | ||||
| import com.google.gerrit.server.account.AccountManager; | ||||
| import com.google.gerrit.server.auth.openid.OpenIdProviderPattern; | ||||
| import com.google.gerrit.server.config.AuthConfig; | ||||
| import com.google.gerrit.server.config.CanonicalWebUrl; | ||||
| import com.google.gerrit.server.config.ConfigUtil; | ||||
| import com.google.gerrit.server.config.GerritServerConfig; | ||||
| import com.google.gwtjsonrpc.common.AsyncCallback; | ||||
| import com.google.gwtorm.client.KeyUtil; | ||||
| import com.google.inject.Inject; | ||||
| import com.google.inject.Provider; | ||||
| @@ -73,7 +69,7 @@ import javax.servlet.http.HttpServletRequest; | ||||
| import javax.servlet.http.HttpServletResponse; | ||||
|  | ||||
| @Singleton | ||||
| class OpenIdServiceImpl implements OpenIdService { | ||||
| class OpenIdServiceImpl { | ||||
|   private static final Logger log = | ||||
|       LoggerFactory.getLogger(OpenIdServiceImpl.class); | ||||
|  | ||||
| @@ -149,19 +145,12 @@ class OpenIdServiceImpl implements OpenIdService { | ||||
|   } | ||||
|  | ||||
|   @SuppressWarnings("unchecked") | ||||
|   public void discover(final String openidIdentifier, final SignInMode mode, | ||||
|       final boolean remember, final String returnToken, | ||||
|       final AsyncCallback<DiscoveryResult> cb) { | ||||
|     if (!isAllowedOpenID(openidIdentifier)) { | ||||
|       cb.onSuccess(new DiscoveryResult(DiscoveryResult.Status.NOT_ALLOWED)); | ||||
|       return; | ||||
|     } | ||||
|  | ||||
|   DiscoveryResult discover(final String openidIdentifier, final SignInMode mode, | ||||
|       final boolean remember, final String returnToken) { | ||||
|     final State state; | ||||
|     state = init(openidIdentifier, mode, remember, returnToken); | ||||
|     if (state == null) { | ||||
|       cb.onSuccess(new DiscoveryResult(DiscoveryResult.Status.NO_PROVIDER)); | ||||
|       return; | ||||
|       return new DiscoveryResult(DiscoveryResult.Status.NO_PROVIDER); | ||||
|     } | ||||
|  | ||||
|     final AuthRequest aReq; | ||||
| @@ -189,16 +178,15 @@ class OpenIdServiceImpl implements OpenIdService { | ||||
|       } | ||||
|     } catch (MessageException e) { | ||||
|       log.error("Cannot create OpenID redirect for " + openidIdentifier, e); | ||||
|       cb.onSuccess(new DiscoveryResult(DiscoveryResult.Status.ERROR)); | ||||
|       return; | ||||
|       return new DiscoveryResult(DiscoveryResult.Status.ERROR); | ||||
|     } catch (ConsumerException e) { | ||||
|       log.error("Cannot create OpenID redirect for " + openidIdentifier, e); | ||||
|       cb.onSuccess(new DiscoveryResult(DiscoveryResult.Status.ERROR)); | ||||
|       return; | ||||
|       return new DiscoveryResult(DiscoveryResult.Status.ERROR); | ||||
|     } | ||||
|  | ||||
|     cb.onSuccess(new DiscoveryResult(aReq.getDestinationUrl(false), // | ||||
|         aReq.getParameterMap())); | ||||
|     return new DiscoveryResult( | ||||
|         aReq.getDestinationUrl(false), | ||||
|         aReq.getParameterMap()); | ||||
|   } | ||||
|  | ||||
|   private boolean requestRegistration(final AuthRequest aReq) { | ||||
| @@ -209,7 +197,6 @@ class OpenIdServiceImpl implements OpenIdService { | ||||
|       // registration information, in case the identity is new to us. | ||||
|       // | ||||
|       return true; | ||||
|  | ||||
|     } | ||||
|  | ||||
|     // We might already have this account on file. Look for it. | ||||
| @@ -222,7 +209,7 @@ class OpenIdServiceImpl implements OpenIdService { | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** Called by {@link OpenIdLoginServlet} doGet, doPost */ | ||||
|   /** Called by {@link OpenIdLoginForm} doGet, doPost */ | ||||
|   void doAuth(final HttpServletRequest req, final HttpServletResponse rsp) | ||||
|       throws Exception { | ||||
|     if (OMODE_CANCEL.equals(req.getParameter(OPENID_MODE))) { | ||||
| @@ -436,7 +423,7 @@ class OpenIdServiceImpl implements OpenIdService { | ||||
|           arsp = accountManager.authenticate(areq); | ||||
|  | ||||
|           final Cookie lastId = new Cookie(OpenIdUrls.LASTID_COOKIE, ""); | ||||
|           lastId.setPath(req.getContextPath() + "/"); | ||||
|           lastId.setPath(req.getContextPath() + "/login/"); | ||||
|           if (remember) { | ||||
|             lastId.setValue(rediscoverIdentifier); | ||||
|             lastId.setMaxAge(LASTID_AGE); | ||||
| @@ -559,7 +546,7 @@ class OpenIdServiceImpl implements OpenIdService { | ||||
|     return new State(discovered, retTo, contextUrl); | ||||
|   } | ||||
|  | ||||
|   private boolean isAllowedOpenID(final String id) { | ||||
|   boolean isAllowedOpenID(final String id) { | ||||
|     for (final OpenIdProviderPattern pattern : allowedOpenIDs) { | ||||
|       if (pattern.matches(id)) { | ||||
|         return true; | ||||
|   | ||||
| @@ -12,8 +12,8 @@ | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
| 
 | ||||
| package com.google.gerrit.common.auth; | ||||
| package com.google.gerrit.httpd.auth.openid; | ||||
| 
 | ||||
| public enum SignInMode { | ||||
| enum SignInMode { | ||||
|   SIGN_IN, LINK_IDENTIY, REGISTER; | ||||
| } | ||||
| @@ -0,0 +1,82 @@ | ||||
| <html> | ||||
|   <head> | ||||
|     <title>Gerrit Code Review - Sign In</title> | ||||
|     <style> | ||||
|       #error_message { | ||||
|         padding: 5px; | ||||
|         margin-left: 5px; | ||||
|         margin-bottom: 5px; | ||||
|         width: 20em; | ||||
|         background-color: rgb(255, 255, 116); | ||||
|         font-weight: bold; | ||||
|       } | ||||
|       #cancel_link { | ||||
|         margin-left: 45px; | ||||
|       } | ||||
|       #logo_box { | ||||
|         padding-left: 160px; | ||||
|       } | ||||
|       #logo_img { | ||||
|         width: 200px; | ||||
|         height: 80px; | ||||
|         background: url('data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAMgAAABQCAYAAABcbTqwAAANbUlEQVR42uxdeXBV1Rm/URYpuNDRFjtuZcaOZTpd7D+O07pi65QqFMQEE7KzhF2kQEUr1gUkIBQiIKDUBYRgUBCCIHEBBLKR5GUlKwlZSAKJtvUPZzr8+r73u80dfHnv3iyveeF9v5lvQsi53zvv3O93z/mWc66hUCgUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCoVCoVAoFAoFQaClAtgQDhSmw1AoFBbwxRZgzgggwgBcShCFwgM0FAGrHgaiDEqcWwoPKEEUChxIBmZ+H5gkxBBRgigUBqozgeX3AU8YQLRb4kWUIAqFgT1LgSlDSI54ESWIQmH5G7EGMFkIoQRRKC4B6l3A1EFArBJEoeiEIIXA1MFKEIVCCaJQKEEUCiWIQqEEUSiUIApFcOdBlCAKhS9cPOtyRJCLWs2rCAVcuHBhakNDA2pqalBWVYOqI3twccogIM4XQfizdv8mlFXXea6T60WPoVD0d5w7dw6VlZUoLCxEbm4ucnJykJWV5ZHMUwUoPrTTEUHKd69DZn5Rx7XZ2dkefS6Xy6O/qalJZxhF/0BjYyNKSko8BmwatBBDRP6vQ7LzC1HySaojglR8kIJsF3Wa0qFTyCKfIf9XVFSEs2fPdoksLS0tOe5ZSQmmCBza2tpGnz59mqTgE96LEJReIoi3eJGlrKwM58+fX274gcw+J0+elKVbjwmCtjogOxVIXw58uJSy9wXgxLtAQ7ESMBQhBiizhRimLSm8CcIlVncIYi8dfZL+WUSxZrm8vLyOGe7MmTPoNjFKPwXWjgeShrJk/09uecSUsQa3DCcOBJbfD2TvuuyJguNvA4nXAnNvAM7XAuVHgZnXUmZcDSQN85YZbll4G/Dyb4B3ZgJZO/v/OMkT2jRCRwbLJZHlgwhBSg/vckSQyg9fcxOkVD7La4ZyShTpryynSktLuezj9T0iCLbNYQRuglmyv2gksH4i8O4sYPtcYHMUsPSXQLzZJtItKROA9st3SYcvNgHhBpBwBdBaDc8DJNqgJIQBiVd6S3wYEGWO0TiDD5qFtwOHU/rfONXV1dHwnBHDy0eQ5Uxzc3Ob6Pq2OtdRmPfb3D34n78gxlxcXIxTp06RLM774UXonhAEKx8CHvfMENwNWbAPPttWnQRem0AjeMwtz7lJc6EWlyVBjmwRA+c2htYaoOwz3t/EAcCpD4DGEqC+0JKGYuBMLpC3Fzi4Clg3Hpj+PWCiQaKt/gPwTdtooz/gO09ff9LRTpY4sqTpUaKw6ONOr5folfTJ/Dyny68eEwSbnhBycNZIewaOr/v8dSBhEG/+S3eFDkFiDGDKQOBcORzpaCoDNkbIA4XjvOqh4B+r/Px8J0/rjjZVVVX4f5aaVFdXW58fQILgy7doAJPckrqg6zPP0TeA6DA+HdMWI5QIIve7a9uxnwciTZJ88GxwjlV7e/tIy/Dso0jl5eXoy1osiaZJXwJGkIU/5g17+e7uO/ZbJlPHtKuAxlIoQXwD68bJw4RO/oW64Bsrifb4NziSR9qJj9DXxYrinwSKIO6QLW9+TFiP6sOEFJg2hCR535pFRCfKj+DSZdkm4PVIYMUDQPKDwJo/AmlLgLo8Z31uqQIOrABSJgLJD4gervPTV3icaK/2/2pNhSsdKP7E6ldzBbB7CbB6DHWsHA1sjoX0LeAEqcuTKCBJcvDV4CKIZKrF+O3IIUbZ19W8sqSziW71nCDrHzOd7J/bt7fXRV/kmVEgaUr4XZ++Ax1kWfgTCRuTSDEGxynCjPQkDga2z/PbDzEozBgOjDfY7xiK+3Opd9YNwOcbqcMiKX2rGdeAUamNwJSh/Mwosw+RnutptItHARXHEBiCEG5SMsK19pEgIQgNTozH1rjECe/Lcvf6+npPX06cOCH96bLIdeK/OOrvkyNoaDvm95wgR9+kIU0ZALRUAs0V/K5L7wRy0oCYK2ioshzL2QWcLZCID/8m5DIjYlgzBp3qf3+RkIlGvPxe4NhWoDYPkALRY//gbBRu+lJ7n4dFkHR+7pzrgf3L2GbezUyAVp4Amko9swu2z5FQLsn75I1A21kEhCD8LvyuC24KHoLYPY3l71Jj1Zf7QVpbW1PFuCV0LLNAN0TIwUSiDXAmlwYRZfRKIsttrEIO6svdLWFfhkLn/wiYeyOQdL3fQ7vx2UYglgYqy59L/pa9E4g0jf8932TGjqekDclYkmHNXHEG+xLnlmX3AO318JEUZJuJdKIDRBAubSOpUx4mwZIE9OuQy89Q2jAF137e6IQrgZos9IrOWdfRwD9ZI0bIsYg2TNKkwcGTldcnXQ20WFFD97KNT9x1Y+11bHicy5fl99DAiw7JmLMP824EvmqAjRPN61+5D+bM2PsEcaVTR/wVXM71NYQAdkur2traUCIIw7O88b1WW4Uld9A/2PeSZNdlLLgsWvmgI/34pn2ke8YhGT56ASaRadwJgxwZJOqLgISBQFwYPMGD019wzCeZOm2AjBSSdMEt/HxZvvUyQYQUMnuLHvl+Rp+Ba3pb30Oy2KG25RZHNsMiSBF6ReezPzMJ8vKlBMlYB8c63p5Gh/nV39NAd86nQ736Yec6XrmXs4As2yTrH+t8pkTeXvosScMCR5DK4+xPTF+/CoN5BL/LKzPXEXoEyU0TQ6DfUJffK/1xO8Jcv3+cLASh7jjDY1RdcPZJqgU300DXjCHp9ix1ruO9uSys3D5PwsxiiCwg/Lf9xjSpckC0tL86UAShXxRrcJYr+7RvbUEcb39LLJlduH8ixAhSeVzWwDSGvL097Q9zC9MG05hObJMkmDjGlHrHMxQNNIYGKpEkLP2VLLHo7C8Z5ZY7/Iv4K3NvYLQqZRxQ6NYXS30OjTfgBJEqaEymDi5v+7akxDZ6JdGjECMIE2izruV6+6MXe04Q3nRWstaeEie7OwTh+jzezMrX5kqY2NTrUOIMVtTGDgY2hgcnQQ6s4LjPHg7xu4KdIAyLhhRBCKy4nw5x8gM9J8hbU4GJVmxf8gsWQQq7OYPUsVI4QnIbfwOaykSXvTQU8edXjTLW1BdMBEkZz3F/8S4EQ/Zcl1g+4PYVaHwJA+39EPvoEwmyNQFmZbMYEY0t/6Ou+iBWFGnV7+i071/Wnf4xURgdPATBV03ArOGcQdKeDv4ciPytoqIiNAkifsJMLrOk5L27eiR8igiWfUhY9ZKxCKeD3fUoFqNW2DabUay1j6JLIeyUOHgSfyWHg2oGcZOCYxUvUbXsvrcDOfQgMzPTX5JQChNDkCCEO2NMI54cBmTt6PoNr84Cpg/j7LF+ArzGIpL1Wd3Og2Sn0kCnDwXO1zjT8+dbgDEGNyxVHg8WgjBBmHgVx2pjuHVtf0gUCpFCjyAEnv81b1rSdUC+44gWy1WeupUz0LwRUk3rTZAYJumk9sn+6bqYumZ8J5O+8Fb2b/Nkex2SxY8wdXzd3MYtsgEiSKPjuj2W0iSZs/X8mzhWQbRz0K7UhMnC0CQIK28X3MYnd8IAIO0vQFs9/N/wDfIGX5NYw4HSDHiNxZRBMh40iOgwSF2T330X8QNpQCw27IBU6CLCrLpNXehbR8E+YOowJgl3LQIN/uOAEUQeEH511eQAh1YDL97Nvj8m5LiN5SXBdgKigzJ3VvKGHkGsPMay34qB8kbO+yGwNRE4vI57rwv2S2aahzcsvl0Mlu2eHgVUfolOxyJxgBgafYHZ11P32rHAyW1ShetVzctl2kR02r83Yq0981K5e/wdqyJYiiPfjJPEG8mx8iHvat6koTBs4d0eR7cCk5hQJUE+tUr1F41k3mXJTztEfpcSf8y8RkLe/E7jDV7z90eB5nIYwQhxxB2UuzOrHsKnu8vJG1h0O594Yz03lwbyhEmIR82S8zk/AHYtBv7JAyt8EIRyvpoHGTx3p+ig0Mioa5wY4BDODn4g+RruB/GhIzYM2BzNQxGsGcEqHbGFd3scE4KYM4Z1aIMI20V1KrzP04aQNG9NB0qYMQ9qFBQUONowJZGvUH/9AfL3AbufATZEcMfdygf59N8xnzPC1+dgMxad5kGQlcqzopJHczfg+nAJ4TquB0NrFXBoDfB6FGeSZFPHh38FqjNtdhR6w6492us5exYd5O/uchUPiVxuKfQhBelA+TEuU/sX6LA7cdolfyL71/X9IDZwRhA9ibE/nchuOuaODoaTDUhKECVISJKEyy37Pd4S4ZLdekoQJUjogJW+QgBHx4ya7STrzuLGHr1hKl0JougHsE4PEXFCFLblsUDizEuCseMFOP+pzfdPkBgzGuQKAYLU5UskjFKXrwTp7687kHN2vY4htSeL9b4QVzHKMt73fXh1FAki8XzJ8BqXOSSCIxW+Iu5/K0H6P3g2rnl2VteP/Mxzdfb6A6vU4tlfAK59aiiK/g85rV2y6vQ9rFmlKy/QEeE5UVdJQk2Jobg8IdErc0+J17s8fBIkyiTHK/fLoQFKDkUogCekiGMuuxStdxaSNJmyxDq4AxeFHLNHABmvKTEUuhST87Qk9FtUVoHTGWm4uCVWjt1UcigUCoVCoVAoFAqFQqFQKBQKhUKhUCgUCsV/24MDEgAAAABB/1+3I1ABAGAilVZ2IKvvzEMAAAAASUVORK5CYII=') no-repeat 0px 0px; | ||||
|       } | ||||
|       #f_openid { | ||||
|         padding-left: 25px; | ||||
|         border: 1px solid #999; | ||||
|         background: #fff url('data:image/gif;base64,R0lGODlhEAAQAMQAAO3t7eHh4srKyvz8/P5pDP9rENLS0v/28P/17tXV1dHEvPDw8M3Nzfn5+d3d3f5jA97Syvnv6MfLzcfHx/1mCPx4Kc/S1Pf189C+tP+xgv/k1N3OxfHy9NLV1/39/f///yH5BAAAAAAALAAAAAAQABAAAAVq4CeOZGme6KhlSDoexdO6H0IUR+otwUYRkMDCUwIYJhLFTyGZJACAwQcgEAQ4kVuEE2AIGAOPQQAQwXCfS8KQGAwMjIYIUSi03B7iJ+AcnmclHg4TAh0QDzIpCw4WGBUZeikDFzk0lpcjIQA7') no-repeat scroll 5px 50% | ||||
|       } | ||||
|     </style> | ||||
|   </head> | ||||
|   <body> | ||||
|     <h1>Sign In to Gerrit Code Review at <span id="hostName">example.com</span></h1> | ||||
| 	<form method="POST" action="#" id="login_form"> | ||||
|       <input type="hidden" name="link" id="f_link" value="1" /> | ||||
| 	  <div id="logo_box"><div id="logo_img"></div></div> | ||||
|       <div id="error_message">Invalid OpenID identifier.</div> | ||||
|       <div> | ||||
|         <input type="text" | ||||
|                name="id" | ||||
|                id="f_openid" | ||||
|                size="60" | ||||
|                tabindex="1" /> | ||||
|       </div> | ||||
|       <div> | ||||
|         <input name="rememberme" id="f_remember" | ||||
|                type="checkbox" | ||||
|                value="1" | ||||
|                tabindex="2" /> | ||||
|         <label for="f_remember">Remember me</label> | ||||
|       </div> | ||||
|       <div style="margin-bottom: 25px;"> | ||||
|         <input type="submit" value="Sign In" id="f_submit" tabindex="3" /> | ||||
|         <a href="../" id="cancel_link">Cancel</a> | ||||
|       </div> | ||||
|  | ||||
|       <div id="provider_google"> | ||||
|         <img height="16" width="16" src="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAABsUlEQVR42rWSy0sbYRRH88fYhW5KQXThIkUQN6ISJFBBW6FFBN2UqlC78LVKFlHIoqWGakF8gUZ8gajVzKSKZpQmAWtK0pBmhkxmSgPuiuaYmRKhSEwU/ODyDZfzO8ydOxbLQx1NB0H4y8FBlmMpy9LyOavrKUoKB4IXvBsIIopRPJ441dY/2Fr8fAtFSxO0dkRy4eA1PDp+TkVlmGhMKS5IJOFpbYgj6fQalhV4UhVm0esvLojELimv1JiYOPwPflS2gM93WNoIXZ1nPC4/IRDQzYDTqTA0PM2dtuCZ8vGmV6SvP5srL/da5c+4TnNLFlvTMYmEVrpkazuM2x1naOQ7Dc1fqLOmaayXkaSz4hLHewm7fY+Z2R0iP+JmYGpym/q6DDb76u0CwR+g9+0FHz4u3wAdLh9VNWu5UX4VlqysfaXRDoODNz+aw7nDs9ZPpNPpwgJV1WhvF6m1/qb79T6fpwXm5kVGRnd5/mIdQSzwH2iahq7/27nxiq5xLy9fbdDWsUtPzyausRVC4VMMzmAMNv9syTdkWTZLVVUymYx5J5NJs5dKpVAUxaw8Z/SM7BV+fz9Uc4yYRAAAAABJRU5ErkJggg==" /> | ||||
|         <a href="?id=https://www.google.com/accounts/o8/id" id="id_google">Sign in with a Google Account</a> | ||||
|       </div> | ||||
|       <div id="provider_yahoo"> | ||||
|         <img height="16" width="16" src="data:image/gif;base64,R0lGODlhEAAQAPECAAAAAP8AAP///8zMzCH5BAEAAAMALAAAAAAQABAAAAIqnI+py30BY3AgAjCkfJDjiIAQlgUkNxqWkqrm0honKk7KhZOzw/f+fygAADs=" /> | ||||
|         <a href="?id=https://me.yahoo.com" id="id_yahoo">Sign in with a Yahoo! ID</a> | ||||
|       </div> | ||||
|  | ||||
|       <div style="margin-top: 25px;"> | ||||
|         <h2>What is OpenID?</h2> | ||||
|         <p>OpenID provides secure single-sign-on, without revealing your passwords to this website.</p> | ||||
|         <p>There are many OpenID providers available.  You may already be member of one!</p> | ||||
|         <p><a href="http://openid.net/get/" target="_blank">Get OpenID</a></p> | ||||
|       </div> | ||||
|     </form> | ||||
|  | ||||
|     <script type="text/javascript" language="javascript"> | ||||
|       var f_openid = document.getElementById('f_openid'); | ||||
|       var f_submit = document.getElementById('f_submit'); | ||||
|       if (f_openid.value == '') | ||||
|         f_openid.focus(); | ||||
|       else | ||||
|         f_submit.focus(); | ||||
|     </script> | ||||
|   </body> | ||||
| </html> | ||||
| @@ -0,0 +1,16 @@ | ||||
| <html> | ||||
|   <head> | ||||
|     <title>Gerrit Code Review - Redirecting ...</title> | ||||
|   </head> | ||||
|   <body> | ||||
|     <div>Redirecting ...</div> | ||||
| 	<form method="POST" action="#" id="redirect_form"> | ||||
| 	  <input type="submit" value="Continue" /> | ||||
|     </form> | ||||
|     <script type="text/javascript" language="javascript"> | ||||
|       var r = document.getElementById('redirect_form'); | ||||
|       r.style.display = 'none'; | ||||
|       r.submit(); | ||||
|     </script> | ||||
|   </body> | ||||
| </html> | ||||
| @@ -12,7 +12,7 @@ | ||||
| // See the License for the specific language governing permissions and | ||||
| // limitations under the License. | ||||
| 
 | ||||
| package com.google.gerrit.common.auth.openid; | ||||
| package com.google.gerrit.server.auth.openid; | ||||
| 
 | ||||
| import com.google.gerrit.reviewdb.client.AccountExternalId; | ||||
| 
 | ||||
| @@ -14,9 +14,9 @@ | ||||
|  | ||||
| package com.google.gerrit.server.config; | ||||
|  | ||||
| import com.google.gerrit.common.auth.openid.OpenIdProviderPattern; | ||||
| import com.google.gerrit.reviewdb.client.AccountExternalId; | ||||
| import com.google.gerrit.reviewdb.client.AuthType; | ||||
| import com.google.gerrit.server.auth.openid.OpenIdProviderPattern; | ||||
| import com.google.gwtjsonrpc.server.SignedToken; | ||||
| import com.google.gwtjsonrpc.server.XsrfException; | ||||
| import com.google.inject.Inject; | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Shawn Pearce
					Shawn Pearce