我在 C# Rest Api 中添加了一个操作指南,解释了如何像 Excel 插件中的 Rally 那样进行 SSO 身份验证。为了方便我把它贴在这里。
谢谢,斯科特
Rally SSO 简介
Rally Web 服务 API (WSAPI) 本身仅支持基本身份验证。使用基本身份验证,WSAPI 会话必须使用用户名和密码启动,该用户名和密码根据直接存储在 Rally 中的用户名和密码列表进行验证。在客户端开始使用单点登录 (SSO) 之前,这种方法可以正常工作。 SSO 允许客户端使用单一企业范围的身份验证机制(如 LDAP 或 Active Directory)来管理用户凭据和密码。到目前为止,使用启用了 SSO 的 WSAPI 要求客户端在 Rally 中为所有希望使用 Rally WSAPI 的用户维护一个重复的用户列表(“白”列表)。最近对 Rally 的更改现在允许 WSAPI 用户使用其 SSO 凭据访问 Rally,并减轻了将这些用户保留在“白”列表中的需要。
注意:Rally 当前的 SSO 实现基于 SAML 规范,该规范要求用户与浏览器交互才能完成身份验证。因此,该技术需要用户与浏览器交互,因此它与无头 WSAPI 客户端(例如那些将 Rally 与 VCS 和错误跟踪工具同步的客户端)不兼容。
启动 SSO 连接时,用户提供一个 URL,该 URL 开始与 Rally 的服务提供商 (SP) 进行 SSO 握手,随后涉及客户端的身份提供商 (IdP),并以 Rally 响应表示有效的经过身份验证的会话的 cookie 结束。如果该经过身份验证的会话 cookie 包含在任何后续 WSAPI 调用中,Rally 会将这些调用与经过身份验证的用户关联起来,并且 WSAPI 调用将得到身份验证。为了在成功的 SAML SSO 身份验证后轻松访问经过身份验证的会话 cookie 以进行 WSAPI 调用,Rally 会查找添加到初始 SSO URL 的参数,如果存在该参数,将返回一个包含明文会话 cookie 的特殊网页文本作为 SSO 握手的最终结果。用户可以使用该数据构建一个 cookie,以便在后续 WSAPI 调用中使用。
注意:下面的示例 URL(可能)特定于 Rally 的内部 SSO 实现。由于 SSO 用于允许客户使用自己的 SSO 基础设施(至少是 IdP 部分)提供自己的身份验证,因此 SSO URL 将是特定于客户的。请联系您的 Rally TAM 或 Rally 支持以获得有关 SSO URL 的帮助。
原始 SSO URL 类似于:
https://sso.rallydev.com/sp/startSSO.ping?PartnerIdpId=pingidp.f4tech.com-29577
特殊参数为:
TargetResource=https://us1.rallydev.com/slm/j_sso_security_check?noRedirect=true
注意:此名称/值对使用 PingIdentity 作为 SSO 提供程序,在 Rally 的特定 SSO 实现中设置 SSO RelayState。其他 SSO 提供商可能有不同的参数名称用于设置 RelayState。例如,某些 SSO 提供商使用 RelayState 作为参数名称。在任何情况下,该值始终相同(即“https://us1.rallydev.com/slm/j_sso_security_check?noRedirect=true”)
所以一个完整的 URL 应该是这样的:
https://sso.rallydev.com/sp/startSSO.ping?PartnerIdpId=pingidp.f4tech.com-29577&TargetResource=https://us1.rallydev.com/slm/j_sso_security_check?noRedirect=true
如果用户导航到此修改后的 SSO URL,则在身份验证后,他们将看到一个包含以下内容的网页:
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN">
<html>
<head>
<title>SSO Token</title>
</head>
<body>
<form>
<input type="hidden" name="authCookieName" value="ZSESSIONID"/>
<input type="hidden" name="authCookieValue" value="khkjhkhkhkhkjhh"/>
</form>
</body>
</html>
如果用户根据此页面中包含的数据创建 cookie,并将该 cookie 与其后续 WSAPI 调用一起传递,则这些调用将成功通过身份验证。请注意,必须根据 IETF cookie 规范从获取 cookie 的调用中推断出安全和路径等可选 cookie 数据。
因此,从 GUI 界面登录 Rally(同样,这不适用于无头环境)以发出 WSAPI 调用的基本过程如下:
- 使用上述特殊参数从用户收集 SSO URL。
- 启动指向该 URL 的浏览器。
- 导航完成后,从返回的 HTML 页面中抓取 cookie 值。
- 关闭浏览器。
- 根据 cookie 值构造 cookie。
- 存储该 cookie 以供以后使用。
- 将该 cookie 与所有后续 WSAPI 调用一起发送。
如果 WSAPI 调用使用此 cookie 进行身份验证失败,请重复此过程。请记住,这些 cookie 会过期,您应该准备好在重新身份验证后重试失败的呼叫,以创建流畅的用户体验。
Rally C# ReST API
Rally C# Rest API 的连接框架中集成了一种机制,使不同 GUI 客户端可以轻松完成此过程,包括会话超时后自动重新身份验证。这包括将令牌页面解析为有效 cookie 的方法。该库的调用者可以实现其他功能,例如推断可选 cookie 数据(如域、路径、安全和主机端口),以解决 Rally 不返回完整 cookie 数据的情况(如测试环境)。
使用 C# ReST API 连接到 Rally 意味着构建 RallyRestApi 的实例。有两个旧版构造函数采用基本身份验证并采用用户名和密码以及其他参数。使用这些构造函数之一构造 RallyRestApi 将始终使用基本身份验证,并且永远不会使用 SSO。
第三个构造函数仅采用 IConnectionInfo 对象。这是获取 RallyRestApi 的首选方法。使用 IConnectionInfo 对象构造 RallyRestApi 允许调用者在一个对象中指定可用于 SSO 回调和多个 RallyRestApi 实例之间的身份验证共享的所有连接信息。
IConnectionInfo 和 ConnectionInfo
为了促进 SSO 身份验证,C# ReST API 引入了 IConnectionInfo 接口和 ConnectionInfo 类。这些类代表一个对象,该对象保存连接首选项,并且可以在请求时启动和完成基于浏览器的 SSO 身份验证会话。 ConnectionInfo 类实现所有连接首选项,并具有将 Rally SSO 登录页面解析为可用 Cookie 的方法。如果只需要基本身份验证,则可以按原样使用此类。 IConnectionInfo 的存在是为了灵活性,以防调用者不想扩展或以其他方式使用 ConnectionInfo。
使用 IConnectionInfo 进行基本身份验证时,只需创建一个新的 ConnectionInfo 并设置适当的可公开访问的字段。使用它来构造 RallyRestApi。任何身份验证错误都会引发异常。
Example:
var cInfo = new ConnectionInfo();
cInfo.UserName = "myName";
cInfo.Password = "pass";
cInfo.Server = new Uri("https://host.com");
cInfo.AuthType = Rally.RestApi.AuthorizationType.Basic;
var conn = new RallyRestApi(cInfo);
当使用 IConnectionInfo 进行 SSO 时,调用者必须实现 DoSSOAuth()。下面是一个带注释的示例。
public class MyConnectionInfo : Rally.RestApi.ConnectionInfo
{
public override void doSSOAuth()
{
// Launch a browser to the this.server URI.
// The browser will close automatically if it successfully reaches the SSO landing page
// Users can cancel the SSO handshake
// Abort if the handshake is successful, but didn't arrive at the SSO landing page
var ssoDialog = new SSOAuthDialog(server);
DialogResult result = ssoDialog.ShowDialog();
if (result == DialogResult.Cancel)
throw new Exception("SSO authorization canceled");
else if (result == DialogResult.Abort)
throw new Exception(ssoDialog.abortReason);
// Parse the SSO landing page into a Cookie and save it
AuthCookie = parseSSOLandingPage(ssoDialog.getBrowser().DocumentText);
// Infer Cookie values from SO Landing Page URL if not set
if (String.IsNullOrWhiteSpace(authCookie.Domain) || authCookie.Domain == "null")
authCookie.Domain = ssoDialog.getBrowser().Url.Host;
AuthCookie.Secure = String.Equals(ssoDialog.getBrowser().Url.Scheme,"https",StringComparison.InvariantCultureIgnoreCase);
// Set a specific port port if the SSO Landing Page URL has one
if (!ssoDialog.getBrowser().Url.IsDefaultPort)
Port = ssoDialog.getBrowser().Url.Port;
}
}
此示例使用带有浏览器组件的 WinForms 对话框向用户呈现 SSO 握手。请记住,您可以使用任何想要实现此部分的显示技术。这是一个带注释的示例:
public partial class SSOAuthDialog : Form
{
public String abortReason;
public SSOAuthDialog(Uri url)
{
InitializeComponent();
webBrowser.Url = url;
}
private void documentCompleted(object sender, WebBrowserDocumentCompletedEventArgs e)
{
// We have found the SSO Landing Page.
if (webBrowser.DocumentText.Contains("authCookieName") && webBrowser.DocumentText.Contains("authCookieValue"))
{
Trace.TraceInformation("Found SSO authentication token on page: {0}", e.Url.AbsolutePath);
DialogResult = DialogResult.OK;
Close();
}
// We have landed on the Rally ALM page
// This is usually caused by a bad URL
else if (webBrowser.DocumentText.Contains("window.FEATURE_TOGGLES"))
{
abortReason = String.Format("The SSO handshake was successful, but the 'RelayState' was not correctly set. Contact your administrator to obtain the correct URL parameter to set the SSO handshake 'RelayState' to: https://rally1.rallydev.com/slm/j_sso_security_check?noRedirect=true");
Trace.TraceError(abortReason);
DialogResult = DialogResult.Abort;
Close();
}
}
public WebBrowser getBrowser()
{
return webBrowser;
}
}
SSO Example:
var cInfo = new MyConnectionInfo();
cInfo.Server = new Uri("https://host");
cInfo.AuthType = Rally.RestApi.AuthorizationType.SSO;
// This will cause an SSO authentication event
var conn = new RallyRestApi(cInfo);
// This will not b/c it will just use the auth Cookie already in cInfo
var conn2 = new RallyRestApi(cInfo);
使用 IConnectionInfo 进行 SSO 时,缓存您发送的用于构造 RallyRestApi 的 IConnectionInfo 对象非常重要。成功 SSO 握手后,生成的 auth Cookie 将存储在 IConnectionInfo 对象中,并将用于从该 RallyRestApi 对象进行的所有后续 WSAPI 调用。如果您需要使用之前成功的 SSO 登录中的相同身份验证 Cookie 创建另一个 RallyRestApi 对象,只需使用相同的 IConnectionInfo 对象构造一个新的 RallyRestApi 对象,如果存在身份验证 Cookie,则会使用该对象。
Retries
授权 Cookie 可能会过期。 C# ReST Api 将检测过期的 SSO Cookie,并根据需要启动新的 SSO 登录会话以获得新的有效 Cookie。
这里的所有都是它的。