CP-13077: Changes to the Health Check feature to work properly with RBAC

Changed all the health check actions to perform the necessary role checks
- HealthCheckAuthenticationAction: changed so it does not save the tokens, so no role checks required in this action (all the checks are performed in SaveHealthCheckSettingsAction)
- GetHealthCheckAnalysisActions: added role checks

On the Health Check Overview dialog, a pool is displayed as read-only if connected as a user with roles that don't permit changing the health check settings:
- the following actions are not available: enroll, edit settings, disable health check, request upload, get analysis result
This commit is contained in:
Mihaela Stoica 2015-09-03 11:49:53 +01:00
parent f772763575
commit 53b01c425c
8 changed files with 148 additions and 106 deletions

View File

@ -44,21 +44,17 @@ namespace XenAdmin.Core
public static event Action<bool> CheckForAnalysisResultsCompleted;
/// <summary>
/// Checks the analysis results for all connections
/// Checks the analysis results for all connections.
/// Will only perform the check for the connection that have sufficient rights; will not show the action progress.
/// </summary>
public static void CheckForAnalysisResults()
{
List<AsyncAction> actions = new List<AsyncAction>();
foreach (IXenConnection xenConnection in ConnectionsManager.XenConnectionsCopy)
{
Pool pool = Helpers.GetPoolOfOne(xenConnection);
if (pool == null || Helpers.FeatureForbidden(xenConnection, Host.RestrictHealthCheck))
continue;
var healthCheckSettings = pool.HealthCheckSettings;
if (healthCheckSettings.Status == HealthCheckStatus.Enabled && !healthCheckSettings.HasAnalysisResult)
actions.Add(new GetHealthCheckAnalysisResultAction(pool, Registry.HealthCheckDiagnosticDomainName, true));
var action = GetAction(xenConnection, true);
if (action != null)
actions.Add(action);
}
if (actions.Count == 1)
@ -75,22 +71,49 @@ namespace XenAdmin.Core
}
/// <summary>
/// Checks the analysis results for the specified connections
/// Checks the analysis results for the specified connections.
/// Will only perform the check if the connection has sufficient rights; will not show the action progress.
/// </summary>
public static void CheckForAnalysisResults(object connection)
{
Pool pool = Helpers.GetPoolOfOne((IXenConnection)connection);
if (pool == null || Helpers.FeatureForbidden((IXenConnection)connection, Host.RestrictHealthCheck))
return;
CheckForAnalysisResults(connection, true);
}
/// <summary>
/// Checks the analysis results for the specified connections.
/// Will only perform the check if the connection has sufficient rights; If suppressHistory is false, it will show the action progress.
/// </summary>
public static void CheckForAnalysisResults(object connection, bool suppressHistory)
{
var action = GetAction((IXenConnection)connection, suppressHistory);
if (action != null)
{
action.Completed += actionCompleted;
action.RunAsync();
}
}
private static AsyncAction GetAction(IXenConnection connection, bool suppressHistory)
{
Pool pool = Helpers.GetPoolOfOne(connection);
if (pool == null || Helpers.FeatureForbidden(connection, Host.RestrictHealthCheck))
return null;
var healthCheckSettings = pool.HealthCheckSettings;
if (healthCheckSettings.Status == HealthCheckStatus.Enabled && !healthCheckSettings.HasAnalysisResult)
{
var action = new GetHealthCheckAnalysisResultAction(pool, Registry.HealthCheckDiagnosticDomainName, true);
action.Completed += actionCompleted;
action.RunAsync();
var action = new GetHealthCheckAnalysisResultAction(pool, Registry.HealthCheckDiagnosticDomainName, suppressHistory);
if (PassedRbacChecks(pool.Connection))
return action;
}
return null;
}
public static bool PassedRbacChecks(IXenConnection connection)
{
return Role.CanPerform(new RbacMethodList("pool.set_health_check_config"), connection);
}
private static void actionCompleted(ActionBase sender)

View File

@ -207,6 +207,8 @@ namespace XenAdmin.Dialogs.HealthCheck
scheduleLabel.Text = GetScheduleDescription(poolRow.Pool.HealthCheckSettings);
lastUploadLabel.Visible = lastUploadDateLabel.Visible = !string.IsNullOrEmpty(poolRow.Pool.HealthCheckSettings.LastSuccessfulUpload);
lastUploadDateLabel.Text = GetLastUploadDescription(poolRow.Pool.HealthCheckSettings);
UpdateButtonsVisibility(poolRow.Pool);
healthCheckStatusPanel.Visible = poolRow.Pool.HealthCheckSettings.Status == HealthCheckStatus.Enabled;
notEnrolledPanel.Visible = poolRow.Pool.HealthCheckSettings.Status != HealthCheckStatus.Enabled;
@ -299,6 +301,16 @@ namespace XenAdmin.Dialogs.HealthCheck
}
}
public void UpdateButtonsVisibility(Pool pool)
{
refreshLinkLabel.Visible =
editLinkLabel.Visible =
disableLinkLabel.Visible =
uploadRequestLinkLabel.Visible =
enrollNowLinkLabel.Visible = Core.HealthCheck.PassedRbacChecks(pool.Connection);
}
private void HealthCheckOverviewDialog_Load(object sender, EventArgs e)
{
LoadPools();
@ -450,7 +462,7 @@ namespace XenAdmin.Dialogs.HealthCheck
if (poolRow.Pool == null)
return;
Core.HealthCheck.CheckForAnalysisResults(poolRow.Pool.Connection);
Core.HealthCheck.CheckForAnalysisResults(poolRow.Pool.Connection, false);
}
private void HealthCheck_CheckForUpdatesCompleted(bool succeeded)

View File

@ -266,9 +266,9 @@ namespace XenAdmin.Dialogs.HealthCheck
if (!CheckCredentialsEntered())
return false;
var action = new HealthCheckAuthenticationAction(pool, textBoxMyCitrixUsername.Text.Trim(), textBoxMyCitrixPassword.Text.Trim(),
var action = new HealthCheckAuthenticationAction(textBoxMyCitrixUsername.Text.Trim(), textBoxMyCitrixPassword.Text.Trim(),
Registry.HealthCheckIdentityTokenDomainName, Registry.HealthCheckUploadGrantTokenDomainName, Registry.HealthCheckUploadTokenDomainName,
Registry.HealthCheckDiagnosticDomainName, Registry.HealthCheckProductKey, true, 0, false);
Registry.HealthCheckDiagnosticDomainName, Registry.HealthCheckProductKey, 0, false);
try
{
@ -283,7 +283,8 @@ namespace XenAdmin.Dialogs.HealthCheck
}
authenticationToken = action.UploadToken; // curent upload token
authenticated = pool.HealthCheckSettings.TryGetExistingTokens(pool.Connection, out authenticationToken, out diagnosticToken);
diagnosticToken = action.DiagnosticToken; // curent diagnostic token
authenticated = !String.IsNullOrEmpty(authenticationToken) && !String.IsNullOrEmpty(diagnosticToken);
return authenticated;
}

View File

@ -183,10 +183,10 @@ namespace XenAdmin.Wizards.BugToolWizardFiles
if (string.IsNullOrEmpty(usernameTextBox.Text) || string.IsNullOrEmpty(passwordTextBox.Text))
return false;
var action = new HealthCheckAuthenticationAction(null, usernameTextBox.Text.Trim(), passwordTextBox.Text.Trim(),
var action = new HealthCheckAuthenticationAction(usernameTextBox.Text.Trim(), passwordTextBox.Text.Trim(),
Registry.HealthCheckIdentityTokenDomainName, Registry.HealthCheckUploadGrantTokenDomainName,
Registry.HealthCheckUploadTokenDomainName, Registry.HealthCheckDiagnosticDomainName, Registry.HealthCheckProductKey,
false, TokenExpiration, false);
TokenExpiration, false);
new ActionProgressDialog(action, ProgressBarStyle.Blocks).ShowDialog(Parent);

View File

@ -49,7 +49,11 @@ namespace XenAdmin.Actions
public GetHealthCheckAnalysisResultAction(Pool pool, bool suppressHistory)
: base(pool.Connection, Messages.ACTION_GET_HEALTH_CHECK_RESULT, Messages.ACTION_GET_HEALTH_CHECK_RESULT_PROGRESS, suppressHistory)
{
#region RBAC Dependencies
ApiMethodsToRoleCheck.Add("secret.get_by_uuid");
ApiMethodsToRoleCheck.Add("secret.get_value");
ApiMethodsToRoleCheck.Add("pool.set_health_check_config");
#endregion
}
public GetHealthCheckAnalysisResultAction(Pool pool, string diagnosticDomainName, bool suppressHistory)
@ -63,7 +67,7 @@ namespace XenAdmin.Actions
{
try
{
var diagnosticToken = Pool.HealthCheckSettings.GetSecretyInfo(Pool.Connection, HealthCheckSettings.DIAGNOSTIC_TOKEN_SECRET);
var diagnosticToken = Pool.HealthCheckSettings.GetSecretyInfo(Session, HealthCheckSettings.DIAGNOSTIC_TOKEN_SECRET);
if (string.IsNullOrEmpty(diagnosticToken))
{
log.InfoFormat("Cannot get the diagnostic result for {0}, because couldn't retrieve the diagnostic token", Pool.Name);

View File

@ -30,14 +30,11 @@
*/
using System;
using System.Collections.Generic;
using System.IO;
using System.Net;
using System.Runtime.Serialization;
using XenAdmin.Network;
using XenAPI;
using System.Web.Script.Serialization;
using XenAdmin.Model;
namespace XenAdmin.Actions
{
@ -45,14 +42,13 @@ namespace XenAdmin.Actions
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly Pool pool;
private readonly string username;
private readonly string password;
private readonly bool saveTokenAsSecret;
private readonly long tokenExpiration;
private string uploadToken;
private string diagnosticToken;
private const string identityTokenUrl = "/auth/api/create_identity/";
private const string uploadGrantTokenUrl = "/feeds/api/create_grant/";
@ -66,25 +62,19 @@ namespace XenAdmin.Actions
private readonly string productKey = "1a2d94a4263cd016dd7a7d510bde87f058a0b75d";
public HealthCheckAuthenticationAction(Pool pool, string username, string password, bool saveTokenAsSecret, long tokenExpiration, bool suppressHistory)
: base(pool != null ? pool.Connection : null, Messages.ACTION_HEALTHCHECK_AUTHENTICATION, Messages.ACTION_HEALTHCHECK_AUTHENTICATION_PROGRESS, suppressHistory)
public HealthCheckAuthenticationAction(string username, string password, long tokenExpiration, bool suppressHistory)
: base(null, Messages.ACTION_HEALTHCHECK_AUTHENTICATION, Messages.ACTION_HEALTHCHECK_AUTHENTICATION_PROGRESS, suppressHistory)
{
this.pool = pool;
this.username = username;
this.password = password;
this.saveTokenAsSecret = saveTokenAsSecret;
this.tokenExpiration = tokenExpiration;
#region RBAC Dependencies
if (saveTokenAsSecret)
ApiMethodsToRoleCheck.Add("pool.set_health_check_config");
#endregion
}
public HealthCheckAuthenticationAction(Pool pool, string username, string password,
public HealthCheckAuthenticationAction(string username, string password,
string identityTokenDomainName, string uploadGrantTokenDomainName, string uploadTokenDomainName, string diagnosticTokenDomainName,
string productKey, bool saveTokenAsSecret, long tokenExpiration, bool suppressHistory)
: this(pool, username, password, saveTokenAsSecret, tokenExpiration, suppressHistory)
string productKey, long tokenExpiration, bool suppressHistory)
: this(username, password, tokenExpiration, suppressHistory)
{
if (!string.IsNullOrEmpty(identityTokenDomainName))
this.identityTokenDomainName = identityTokenDomainName;
@ -100,22 +90,12 @@ namespace XenAdmin.Actions
protected override void Run()
{
System.Diagnostics.Trace.Assert(pool != null || !saveTokenAsSecret, "Pool is null! Cannot save token as secret");
try
{
string identityToken = GetIdentityToken();
string uploadGrantToken = GetUploadGrantToken(identityToken);
uploadToken = GetUploadToken(uploadGrantToken);
string diagnosticToken = GetDiagnosticToken(identityToken);
if (saveTokenAsSecret && pool != null)
{
log.Info("Saving upload token as xapi secret");
Dictionary<string, string> newConfig = pool.health_check_config;
SetSecretInfo(Connection, newConfig, HealthCheckSettings.UPLOAD_TOKEN_SECRET, uploadToken); log.Info("Saving upload token as xapi secret");
SetSecretInfo(Connection, newConfig, HealthCheckSettings.DIAGNOSTIC_TOKEN_SECRET, diagnosticToken);
Pool.set_health_check_config(Connection.Session, pool.opaque_ref, newConfig);
}
diagnosticToken = GetDiagnosticToken(identityToken);
}
catch (HealthCheckAuthenticationException)
{
@ -132,53 +112,9 @@ namespace XenAdmin.Actions
get { return uploadToken; }
}
public static void SetSecretInfo(IXenConnection connection, Dictionary<string, string> config, string infoKey, string infoValue)
public string DiagnosticToken
{
if (string.IsNullOrEmpty(infoKey))
return;
if (infoValue == null)
{
if (config.ContainsKey(infoKey))
{
TryToDestroySecret(connection, config[infoKey]);
config.Remove(infoKey);
}
}
else if (config.ContainsKey(infoKey))
{
try
{
string secretRef = Secret.get_by_uuid(connection.Session, config[infoKey]);
Secret.set_value(connection.Session, secretRef, infoValue);
}
catch (Failure)
{
config[infoKey] = Secret.CreateSecret(connection.Session, infoValue);
}
catch (WebException)
{
config[infoKey] = Secret.CreateSecret(connection.Session, infoValue);
}
}
else
{
config[infoKey] = Secret.CreateSecret(connection.Session, infoValue);
}
}
private static void TryToDestroySecret(IXenConnection connection, string secret_uuid)
{
try
{
var secret = Secret.get_by_uuid(connection.Session, secret_uuid);
Secret.destroy(connection.Session, secret.opaque_ref);
log.DebugFormat("Successfully destroyed secret {0}", secret_uuid);
}
catch (Exception exn)
{
log.Error(string.Format("Failed to destroy secret {0}", secret_uuid), exn);
}
get { return diagnosticToken; }
}
private string GetIdentityToken()

View File

@ -29,7 +29,9 @@
* SUCH DAMAGE.
*/
using System;
using System.Collections.Generic;
using System.Net;
using XenAdmin.Model;
using XenAPI;
@ -37,6 +39,8 @@ namespace XenAdmin.Actions
{
public class SaveHealthCheckSettingsAction : PureAsyncAction
{
private static readonly log4net.ILog log = log4net.LogManager.GetLogger(System.Reflection.MethodBase.GetCurrentMethod().DeclaringType);
private readonly Pool pool;
HealthCheckSettings healthCheckSettings;
private string authenticationToken;
@ -59,12 +63,67 @@ namespace XenAdmin.Actions
{
Dictionary<string, string> newConfig = healthCheckSettings.ToDictionary(pool.health_check_config);
if (!string.IsNullOrEmpty(authenticationToken))
HealthCheckAuthenticationAction.SetSecretInfo(Connection, newConfig, HealthCheckSettings.UPLOAD_TOKEN_SECRET, authenticationToken);
{
log.Info("Saving upload token as xapi secret");
SetSecretInfo(Session, newConfig, HealthCheckSettings.UPLOAD_TOKEN_SECRET, authenticationToken);
}
if (!string.IsNullOrEmpty(diagnosticToken))
HealthCheckAuthenticationAction.SetSecretInfo(Connection, newConfig, HealthCheckSettings.DIAGNOSTIC_TOKEN_SECRET, diagnosticToken);
HealthCheckAuthenticationAction.SetSecretInfo(Connection, newConfig, HealthCheckSettings.UPLOAD_CREDENTIAL_USER_SECRET, username);
HealthCheckAuthenticationAction.SetSecretInfo(Connection, newConfig, HealthCheckSettings.UPLOAD_CREDENTIAL_PASSWORD_SECRET, password);
Pool.set_health_check_config(Connection.Session, pool.opaque_ref, newConfig);
{
log.Info("Saving diagnostic token as xapi secret");
SetSecretInfo(Session, newConfig, HealthCheckSettings.DIAGNOSTIC_TOKEN_SECRET, diagnosticToken);
}
SetSecretInfo(Session, newConfig, HealthCheckSettings.UPLOAD_CREDENTIAL_USER_SECRET, username);
SetSecretInfo(Session, newConfig, HealthCheckSettings.UPLOAD_CREDENTIAL_PASSWORD_SECRET, password);
Pool.set_health_check_config(Session, pool.opaque_ref, newConfig);
}
public static void SetSecretInfo(Session session, Dictionary<string, string> config, string infoKey, string infoValue)
{
if (string.IsNullOrEmpty(infoKey))
return;
if (infoValue == null)
{
if (config.ContainsKey(infoKey))
{
TryToDestroySecret(session, config[infoKey]);
config.Remove(infoKey);
}
}
else if (config.ContainsKey(infoKey))
{
try
{
string secretRef = Secret.get_by_uuid(session, config[infoKey]);
Secret.set_value(session, secretRef, infoValue);
}
catch (Failure)
{
config[infoKey] = Secret.CreateSecret(session, infoValue);
}
catch (WebException)
{
config[infoKey] = Secret.CreateSecret(session, infoValue);
}
}
else
{
config[infoKey] = Secret.CreateSecret(session, infoValue);
}
}
private static void TryToDestroySecret(Session session, string secret_uuid)
{
try
{
var secret = Secret.get_by_uuid(session, secret_uuid);
Secret.destroy(session, secret.opaque_ref);
log.DebugFormat("Successfully destroyed secret {0}", secret_uuid);
}
catch (Exception exn)
{
log.Error(string.Format("Failed to destroy secret {0}", secret_uuid), exn);
}
}
}
}

View File

@ -272,7 +272,7 @@ namespace XenAdmin.Model
#endregion
public string GetSecretyInfo(IXenConnection connection, string secretType)
public string GetSecretyInfo(Session session, string secretType)
{
string UUID = string.Empty;
switch (secretType)
@ -294,12 +294,12 @@ namespace XenAdmin.Model
break;
}
if (connection == null || string.IsNullOrEmpty(UUID))
if (session == null || string.IsNullOrEmpty(UUID))
return null;
try
{
string opaqueref = Secret.get_by_uuid(connection.Session, UUID);
return Secret.get_value(connection.Session, opaqueref);
string opaqueref = Secret.get_by_uuid(session, UUID);
return Secret.get_value(session, opaqueref);
}
catch (Exception e)
{
@ -308,6 +308,13 @@ namespace XenAdmin.Model
}
}
public string GetSecretyInfo(IXenConnection connection, string secretType)
{
if (connection == null)
return null;
return GetSecretyInfo(connection.Session, secretType);
}
public bool TryGetExistingTokens(IXenConnection connection, out string uploadToken, out string diagnosticToken)
{
uploadToken = null;