/* 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.Text; using System.Windows.Forms; using XenAdmin.Network; using XenAPI; using XenAdmin.Core; using XenAdmin.Dialogs; using System.Drawing; namespace XenAdmin.Commands { // The only difference between a CrossConnectionCommand and a Command is that // it first checks that the user can do the necessary operation on all the // connections, and if not, it throws a dialog and gives up. // // At the moment, this does a very basic check, which is good enough for folders and tags: // it just checks that the user has better than Read Only access on all affected objects. // This breaks the abstraction in the Role code, but to calculate exactly which operations // are required on which objects is quite complicated (although it will probably become // necessary when we have more granular RBAC). internal abstract class CrossConnectionCommand : Command { // All the constructors just pass their arguments through to the base constructor protected CrossConnectionCommand() : base() {} protected CrossConnectionCommand(IMainWindow mainWindow) : base(mainWindow) {} protected CrossConnectionCommand(IMainWindow mainWindow, IEnumerable selection) : base(mainWindow, selection) {} protected CrossConnectionCommand(IMainWindow mainWindow, IXenObject selection) : base(mainWindow, selection) {} protected override bool Confirm() { List failedConnections = FailedConnections(); if (failedConnections.Count > 0) { if (!Program.RunInAutomatedTestMode) using (var dlg = new ThreeButtonDialog( new ThreeButtonDialog.Details( SystemIcons.Error, MessageBoxMessage(failedConnections), Messages.PERMISSION_DENIED))) { dlg.ShowDialog(Program.MainWindow); } return false; // returning false stops the command from proceeding } else return base.Confirm(); } /// The list of connections that we didn't have permission on private List FailedConnections() { Dictionary allConnections = new Dictionary(); foreach (IXenObject o in GetAffectedObjects()) allConnections[o.Connection] = true; List failedConnections = new List(); foreach (IXenConnection connection in allConnections.Keys) { if (IsReadOnly(connection)) failedConnections.Add(connection); } return failedConnections; } /// /// Find the list of connections to operate on. /// protected abstract List GetAffectedObjects(); /// /// Is the user Read Only on this connection? This should not normally be used: /// use the code in Role.cs instead, which is more abstract. /// public static bool IsReadOnly(IXenConnection connection) { return connection.Session.Roles.Find( delegate(Role r) { return r.name_label.ToLowerInvariant() == Role.MR_ROLE_READ_ONLY; }) != null; } private string MessageBoxMessage(List connections) { if (connections.Count == 1) return string.Format(Messages.READ_ONLY_ON_SINGULAR, Helpers.GetName(connections[0])); else return string.Format(Messages.READ_ONLY_ON_PLURAL, Helpers.StringifyList(connections.ConvertAll(delegate(IXenConnection c) { return Helpers.GetName(c); }))); } } }