From 2a407f65f7203497c21b1e2e860d7e61ff11b821 Mon Sep 17 00:00:00 2001 From: Sebastian Sdorra Date: Fri, 7 Jan 2011 13:12:26 +0100 Subject: [PATCH] added change password dialog --- .../sonia/scm/api/rest/RestActionResult.java | 95 ++++++++++ .../resources/ChangePasswordResource.java | 170 ++++++++++++++++++ scm-webapp/src/main/webapp/index.html | 1 + .../main/webapp/resources/js/sonia.action.js | 125 +++++++++++++ .../src/main/webapp/resources/js/sonia.scm.js | 52 ++++-- 5 files changed, 430 insertions(+), 13 deletions(-) create mode 100644 scm-webapp/src/main/java/sonia/scm/api/rest/RestActionResult.java create mode 100644 scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java create mode 100644 scm-webapp/src/main/webapp/resources/js/sonia.action.js diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/RestActionResult.java b/scm-webapp/src/main/java/sonia/scm/api/rest/RestActionResult.java new file mode 100644 index 0000000000..ab806f3498 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/RestActionResult.java @@ -0,0 +1,95 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.api.rest; + +//~--- JDK imports ------------------------------------------------------------ + +import javax.xml.bind.annotation.XmlRootElement; + +/** + * + * @author Sebastian Sdorra + */ +@XmlRootElement(name = "result") +public class RestActionResult +{ + + /** + * Constructs ... + * + */ + public RestActionResult() {} + + /** + * Constructs ... + * + * + * @param success + */ + public RestActionResult(boolean success) + { + this.success = success; + } + + //~--- get methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @return + */ + public boolean isSuccess() + { + return success; + } + + //~--- set methods ---------------------------------------------------------- + + /** + * Method description + * + * + * @param success + */ + public void setSuccess(boolean success) + { + this.success = success; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private boolean success = false; +} diff --git a/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java new file mode 100644 index 0000000000..52bb1aa595 --- /dev/null +++ b/scm-webapp/src/main/java/sonia/scm/api/rest/resources/ChangePasswordResource.java @@ -0,0 +1,170 @@ +/** + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + + + +package sonia.scm.api.rest.resources; + +//~--- non-JDK imports -------------------------------------------------------- + +import com.google.inject.Inject; +import com.google.inject.Provider; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import sonia.scm.api.rest.RestActionResult; +import sonia.scm.security.EncryptionHandler; +import sonia.scm.user.User; +import sonia.scm.user.UserException; +import sonia.scm.user.UserManager; +import sonia.scm.user.xml.XmlUserManager; +import sonia.scm.util.AssertUtil; +import sonia.scm.web.security.WebSecurityContext; + +//~--- JDK imports ------------------------------------------------------------ + +import java.io.IOException; + +import javax.ws.rs.FormParam; +import javax.ws.rs.POST; +import javax.ws.rs.Path; +import javax.ws.rs.Produces; +import javax.ws.rs.WebApplicationException; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; + +/** + * + * @author Sebastian Sdorra + */ +@Path("action/change-password") +public class ChangePasswordResource +{ + + /** the logger for ChangePasswordResource */ + private static final Logger logger = + LoggerFactory.getLogger(ChangePasswordResource.class); + + //~--- constructors --------------------------------------------------------- + + /** + * Constructs ... + * + * + * @param userManager + * @param encryptionHandler + * @param securityContextProvider + */ + @Inject + public ChangePasswordResource( + UserManager userManager, EncryptionHandler encryptionHandler, + Provider securityContextProvider) + { + this.userManager = userManager; + this.encryptionHandler = encryptionHandler; + this.securityContextProvider = securityContextProvider; + } + + //~--- methods -------------------------------------------------------------- + + /** + * Method description + * + * + * @param oldPassword + * @param newPassword + * + * @return + * + * @throws IOException + * @throws UserException + */ + @POST + @Produces({ MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON }) + public Response changePassword(@FormParam("old-password") String oldPassword, + @FormParam("new-password") String newPassword) + throws UserException, IOException + { + AssertUtil.assertIsNotEmpty(oldPassword); + AssertUtil.assertIsNotEmpty(newPassword); + + int length = newPassword.length(); + + if ((length < 6) || (length > 32)) + { + throw new WebApplicationException(Response.Status.BAD_REQUEST); + } + + Response response = null; + WebSecurityContext securityContext = securityContextProvider.get(); + User currentUser = securityContext.getUser(); + + if (logger.isInfoEnabled()) + { + logger.info("password change for user {}", currentUser.getName()); + } + + if (currentUser.getType().equals(XmlUserManager.TYPE)) + { + User dbUser = userManager.get(currentUser.getName()); + + if (encryptionHandler.encrypt(oldPassword).equals(dbUser.getPassword())) + { + dbUser.setPassword(encryptionHandler.encrypt(newPassword)); + userManager.modify(dbUser); + response = Response.ok(new RestActionResult(true)).build(); + } + else + { + response = Response.status(Response.Status.BAD_REQUEST).build(); + } + } + else + { + logger.error("only xml user can change their passwor"); + response = Response.status(Response.Status.INTERNAL_SERVER_ERROR).build(); + } + + return response; + } + + //~--- fields --------------------------------------------------------------- + + /** Field description */ + private EncryptionHandler encryptionHandler; + + /** Field description */ + private Provider securityContextProvider; + + /** Field description */ + private UserManager userManager; +} diff --git a/scm-webapp/src/main/webapp/index.html b/scm-webapp/src/main/webapp/index.html index b7ce4c6ccb..33a45f3050 100644 --- a/scm-webapp/src/main/webapp/index.html +++ b/scm-webapp/src/main/webapp/index.html @@ -58,6 +58,7 @@ + diff --git a/scm-webapp/src/main/webapp/resources/js/sonia.action.js b/scm-webapp/src/main/webapp/resources/js/sonia.action.js new file mode 100644 index 0000000000..da064fd70e --- /dev/null +++ b/scm-webapp/src/main/webapp/resources/js/sonia.action.js @@ -0,0 +1,125 @@ +/* * + * Copyright (c) 2010, Sebastian Sdorra + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * 1. Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * 3. Neither the name of SCM-Manager; nor the names of its + * contributors may be used to endorse or promote products derived from this + * software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" + * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE + * DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY + * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES + * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON + * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * http://bitbucket.org/sdorra/scm-manager + * + */ + +Ext.ns('Sonia.action'); + +Sonia.action.ChangePasswordWindow = Ext.extend(Ext.Window,{ + + initComponent: function(){ + + var config = { + layout:'fit', + width:300, + height:170, + closable: false, + resizable: false, + plain: true, + border: false, + modal: true, + items: [{ + id: 'changePasswordForm', + url: restUrl + 'action/change-password.json', + title: 'Change Password', + frame: true, + xtype: 'form', + monitorValid: true, + defaultType: 'textfield', + items: [{ + name: 'old-password', + fieldLabel: 'Old Password', + inputType: 'password', + allowBlank: false, + minLength: 6, + maxLength: 32 + },{ + id: 'new-password', + name: 'new-password', + fieldLabel: 'New Password', + inputType: 'password', + allowBlank: false, + minLength: 6, + maxLength: 32 + },{ + name: 'confirm-password', + fieldLabel: 'Confirm Password', + inputType: 'password', + allowBlank: false, + minLength: 6, + maxLength: 32, + vtype: 'password', + initialPassField: 'new-password' + }], + buttons: [{ + text: 'Ok', + formBind: true, + scope: this, + handler: this.changePassword + },{ + text: 'Cancel', + scope: this, + handler: this.cancel + }] + }] + } + + Ext.apply(this, Ext.apply(this.initialConfig, config)); + Sonia.action.ChangePasswordWindow.superclass.initComponent.apply(this, arguments); + }, + + changePassword: function(){ + var win = this; + var form = Ext.getCmp('changePasswordForm').getForm(); + form.submit({ + method:'POST', + waitTitle:'Connecting', + waitMsg:'Sending data...', + + success: function(form, action){ + if ( debug ){ + console.debug( 'change password success' ); + } + win.close(); + }, + + failure: function(form, action){ + if ( debug ){ + console.debug( 'change password failed' ); + } + Ext.Msg.alert('change password failed!'); + } + }); + }, + + cancel: function(){ + this.close(); + } + +}); \ No newline at end of file diff --git a/scm-webapp/src/main/webapp/resources/js/sonia.scm.js b/scm-webapp/src/main/webapp/resources/js/sonia.scm.js index 1e023056e3..b443c7cf77 100644 --- a/scm-webapp/src/main/webapp/resources/js/sonia.scm.js +++ b/scm-webapp/src/main/webapp/resources/js/sonia.scm.js @@ -120,7 +120,22 @@ Ext.onReady(function(){ }] }); + var securitySection = null; + + if ( state.user.type == 'xml' && state.user.name != 'anonymous' ){ + securitySection = { + title: 'Security', + items: [{ + label: 'Change Password', + fn: function(){ + new Sonia.action.ChangePasswordWindow().show(); + } + }] + } + } + if ( admin ){ + panel.addSections([{ id: 'navConfig', title: 'Config', @@ -140,20 +155,31 @@ Ext.onReady(function(){ addTabPanel('plugins', 'pluginGrid', 'Plugins'); } }] - },{ - title: 'Security', - items: [{ - label: 'Users', - fn: function(){ - addTabPanel('users', 'userPanel', 'Users'); - } - },{ - label: 'Groups', - fn: function(){ - addTabPanel('groups', 'groupPanel', 'Groups'); - } - }] }]); + + if ( securitySection == null ){ + securitySection = { + title: 'Security', + items: [] + } + } + + securitySection.items.push({ + label: 'Users', + fn: function(){ + addTabPanel('users', 'userPanel', 'Users'); + } + }); + securitySection.items.push({ + label: 'Groups', + fn: function(){ + addTabPanel('groups', 'groupPanel', 'Groups'); + } + }); + } + + if ( securitySection != null ){ + panel.addSection( securitySection ); } if ( state.user.name == 'anonymous' ){