/* Copyright (c) Citrix Systems Inc. * All rights reserved. * * Redistribution and use in source and binary forms, * with or without modification, are permitted provided * that the following conditions are met: * * * Redistributions of source code must retain the above * copyright notice, this list of conditions and the * following disclaimer. * * 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. * * 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 COPYRIGHT HOLDER 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. */ using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; using XenAPI; using XenAdmin.Actions; using XenAdmin.Network; using XenAdmin.Core; namespace XenAdmin.Dialogs { public partial class RoleElevationDialog : XenDialogBase { private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType); public Session elevatedSession = null; public string elevatedPassword; public string elevatedUsername; public string originalUsername; public string originalPassword; private List authorizedRoles; /// /// Displays a dialog informing the user they need a different role to complete the task, and offers the chance to switch user. Optionally logs /// out the elevated session. If successful exposes the elevated session, password and username as fields. /// /// The current server connection with the role information /// The session on which we have been denied access /// A list of roles that are able to complete the task /// A description of the current action, if null or empty will not be displayed public RoleElevationDialog(IXenConnection connection, Session session, List authorizedRoles, string actionTitle) { InitializeComponent(); Image icon = SystemIcons.Exclamation.ToBitmap(); pictureBox1.Image = icon; pictureBox1.Width = icon.Width; pictureBox1.Height = icon.Height; this.connection = connection; UserDetails ud = session.CurrentUserDetails; labelCurrentUserValue.Text = ud.UserDisplayName ?? ud.UserName ?? Messages.UNKNOWN_AD_USER; labelCurrentRoleValue.Text = Role.FriendlyCSVRoleList(session.Roles); authorizedRoles.Sort((r1, r2) => r2.CompareTo(r1)); labelRequiredRoleValue.Text = Role.FriendlyCSVRoleList(authorizedRoles); labelServerValue.Text = Helpers.GetName(connection); labelServer.Text = Helpers.IsPool(connection) ? Messages.POOL_COLON : Messages.SERVER_COLON; originalUsername = session.Connection.Username; originalPassword = session.Connection.Password; if (string.IsNullOrEmpty(actionTitle)) { labelCurrentAction.Visible = false; labelCurrentActionValue.Visible = false; } else { labelCurrentActionValue.Text = actionTitle; } this.authorizedRoles = authorizedRoles; } private void buttonAuthorize_Click(object sender, EventArgs e) { try { Exception delegateException = null; log.Debug("Testing logging in with the new credentials"); DelegatedAsyncAction loginAction = new DelegatedAsyncAction(connection, Messages.AUTHORIZING_USER, Messages.CREDENTIALS_CHECKING, Messages.CREDENTIALS_CHECK_COMPLETE, delegate { try { elevatedSession = connection.ElevatedSession(TextBoxUsername.Text.Trim(), TextBoxPassword.Text); } catch (Exception ex) { delegateException = ex; } }); using (var dlg = new ActionProgressDialog(loginAction, ProgressBarStyle.Marquee, false)) dlg.ShowDialog(this); // The exception would have been handled by the action progress dialog, just return the user to the sudo dialog if (loginAction.Exception != null) return; if(HandledAnyDelegateException(delegateException)) return; if (elevatedSession.IsLocalSuperuser || SessionAuthorized(elevatedSession)) { elevatedUsername = TextBoxUsername.Text.Trim(); elevatedPassword = TextBoxPassword.Text; DialogResult = DialogResult.OK; Close(); return; } ShowNotAuthorisedDialog(); return; } catch (Exception ex) { log.DebugFormat("Exception when attempting to sudo action: {0} ", ex); using (var dlg = new ThreeButtonDialog( new ThreeButtonDialog.Details( SystemIcons.Error, String.Format(Messages.USER_AUTHORIZATION_FAILED, TextBoxUsername.Text), Messages.XENCENTER))) { dlg.ShowDialog(Parent); } } finally { // Check whether we have a successful elevated session and whether we have been asked to log it out // If non successful (most likely the new subject is not authorized) then log it out anyway. if (elevatedSession != null && DialogResult != DialogResult.OK) { elevatedSession.Connection.Logout(elevatedSession); elevatedSession = null; } } } private bool HandledAnyDelegateException(Exception delegateException) { if (delegateException != null) { Failure f = delegateException as Failure; if (f != null && f.ErrorDescription[0] == Failure.RBAC_PERMISSION_DENIED) { ShowNotAuthorisedDialog(); return true; } throw delegateException; } return false; } private void ShowNotAuthorisedDialog() { using (var dlg = new ThreeButtonDialog( new ThreeButtonDialog.Details( SystemIcons.Error, Messages.USER_NOT_AUTHORIZED, Messages.PERMISSION_DENIED))) { dlg.ShowDialog(this); } } private bool SessionAuthorized(Session s) { UserDetails ud = s.CurrentUserDetails; foreach (Role r in s.Roles) { if (authorizedRoles.Contains(r)) { log.DebugFormat("Subject '{0}' is authorized to complete the action", ud.UserDisplayName ?? ud.UserName ?? ud.UserSid); return true; } } log.DebugFormat("Subject '{0}' is not authorized to complete the action", ud.UserDisplayName ?? ud.UserName ?? ud.UserSid); return false; } private void buttonCancel_Click(object sender, EventArgs e) { DialogResult = DialogResult.Cancel; Close(); } private void UpdateButtons() { buttonAuthorize.Enabled = TextBoxUsername.Text.Trim() != "" && TextBoxPassword.Text != ""; } private void TextBoxUsername_TextChanged(object sender, EventArgs e) { UpdateButtons(); } private void TextBoxPassword_TextChanged(object sender, EventArgs e) { UpdateButtons(); } } }