From f759dd8caf0b1025ed7653df0e8461e94ae6483a Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Mon, 9 Feb 2015 14:13:46 -0600 Subject: [PATCH 001/103] Rename PairingStatus to Pairing --- README.md | 2 +- ToopherDotNet/ToopherDotNet.cs | 14 +++++++------- ToopherDotNetDemo/ToopherDotNetDemo.cs | 8 ++++---- ToopherDotNetTests/ToopherDotNetTests.cs | 12 ++++++------ ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs | 8 ++++---- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/README.md b/README.md index 89ac845..fe25c29 100644 --- a/README.md +++ b/README.md @@ -32,7 +32,7 @@ using Toopher; ToopherApi api = new ToopherApi("", ""); // Step 1 - Pair with their phone's Toopher app -PairingStatus pairing = api.Pair("pairing phrase", "username@yourservice.com"); +Pairing pairing = api.Pair("pairing phrase", "username@yourservice.com"); // Step 2 - Authenticate a log in AuthenticationStatus auth = api.Authenticate(pairing.id, "my computer"); diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 87c2daa..effde06 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -55,7 +55,7 @@ public ToopherAPI (string consumerKey, string consumerSecret, string baseUrl = n // // Must provide a pairing request (generated on their phone) and // the user's name - public PairingStatus Pair (string pairingPhrase, string userName, Dictionary extras = null) + public Pairing Pair (string pairingPhrase, string userName, Dictionary extras = null) { string endpoint = "pairings/create"; @@ -70,18 +70,18 @@ public PairingStatus Pair (string pairingPhrase, string userName, Dictionary _dict; @@ -366,10 +366,10 @@ public bool enabled public override string ToString () { - return string.Format ("[PairingStatus: id={0}; userId={1}; userName={2}, enabled={3}, pending={4}]", id, userId, userName, enabled, pending); + return string.Format ("[Pairing: id={0}; userId={1}; userName={2}, enabled={3}, pending={4}]", id, userId, userName, enabled, pending); } - public PairingStatus (IDictionary _dict) + public Pairing (IDictionary _dict) { try { this._dict = _dict; diff --git a/ToopherDotNetDemo/ToopherDotNetDemo.cs b/ToopherDotNetDemo/ToopherDotNetDemo.cs index 9e56a62..fd659f0 100644 --- a/ToopherDotNetDemo/ToopherDotNetDemo.cs +++ b/ToopherDotNetDemo/ToopherDotNetDemo.cs @@ -58,8 +58,8 @@ public static void Main (string[] args) Console.WriteLine ("Sending pairing request..."); try { - var pairingStatus = api.Pair (pairingPhrase, userName); - pairingId = pairingStatus.id; + var pairing = api.Pair (pairingPhrase, userName); + pairingId = pairing.id; break; } catch (RequestError err) { System.Console.WriteLine (String.Format ("The pairing phrase was not accepted (reason:{0})", err.Message)); @@ -72,8 +72,8 @@ public static void Main (string[] args) Console.WriteLine ("Checking status of pairing request..."); try { - var pairingStatus = api.GetPairingStatus (pairingId); - if (pairingStatus.enabled) { + var pairing = api.GetPairing (pairingId); + if (pairing.enabled) { Console.WriteLine ("Pairing complete"); break; } else { diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 38ca64d..a14d06a 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -109,7 +109,7 @@ public void CreatePairingTest () { var api = getApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; - PairingStatus pairing = api.Pair ("awkward turtle", "some user"); + Pairing pairing = api.Pair ("awkward turtle", "some user"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["pairing_phrase"], "awkward turtle"); Assert.AreEqual (pairing.id, "1"); @@ -149,7 +149,7 @@ public void ArbitraryParametersOnPairTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; - PairingStatus pairing = api.Pair ("awkward turtle", "some user", extras: new Dictionary(){{"test_param", "42"}}); + Pairing pairing = api.Pair ("awkward turtle", "some user", extras: new Dictionary(){{"test_param", "42"}}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); } @@ -165,11 +165,11 @@ public void ArbitraryParamtersOnAuthenticateTest () } [Test] - public void AccessArbitraryKeysInPairingStatusTest () + public void AccessArbitraryKeysInPairingTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}, ""random_key"":""84""}"; - PairingStatus pairing = api.GetPairingStatus ("1"); + Pairing pairing = api.GetPairing ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (pairing.id, "1"); Assert.AreEqual (pairing.userName, "some user"); @@ -271,11 +271,11 @@ public void GetAuthenticationStatusWIthOtpTest () [Test] - public void PairingStatusTest () + public void PairingTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; - PairingStatus pairing = api.GetPairingStatus ("1"); + Pairing pairing = api.GetPairing ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (pairing.id, "1"); Assert.AreEqual (pairing.userName, "some user"); diff --git a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs index d10a6f6..0ed51be 100644 --- a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs +++ b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs @@ -67,8 +67,8 @@ public static void Main (string[] args) string terminalIdentifier = random.Next().ToString(); state = STATE.AUTHENTICATE; - PairingStatus pairingStatus = null; AuthenticationStatus authStatus = null; + Pairing pairing = null; while (true) { switch (state) { @@ -167,7 +167,7 @@ public static void Main (string[] args) } try { - pairingStatus = api.Pair (pairingPhrase, userName); + pairing = api.Pair (pairingPhrase, userName); state = STATE.POLL_FOR_PAIRING; break; } catch (RequestError err) { @@ -176,8 +176,8 @@ public static void Main (string[] args) break; }; case STATE.POLL_FOR_PAIRING: { - pairingStatus = api.GetPairingStatus(pairingStatus.id); - if (pairingStatus.enabled) { + pairing = api.GetPairing(pairing.id); + if (pairing.enabled) { Console.WriteLine ("Pairing complete"); state = STATE.AUTHENTICATE; } else { From 9db9440111c88ebd54b408162010e1938754cb7d Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Mon, 9 Feb 2015 14:14:58 -0600 Subject: [PATCH 002/103] Rename AuthenticationStatus to AuthenticationRequest --- README.md | 6 +++--- ToopherDotNet/ToopherDotNet.cs | 20 +++++++++---------- ToopherDotNetDemo/ToopherDotNetDemo.cs | 4 ++-- ToopherDotNetTests/ToopherDotNetTests.cs | 18 ++++++++--------- .../ToopherZeroStorageDemo.cs | 6 +++--- 5 files changed, 27 insertions(+), 27 deletions(-) diff --git a/README.md b/README.md index fe25c29..f79995f 100644 --- a/README.md +++ b/README.md @@ -35,11 +35,11 @@ ToopherApi api = new ToopherApi("", "") Pairing pairing = api.Pair("pairing phrase", "username@yourservice.com"); // Step 2 - Authenticate a log in -AuthenticationStatus auth = api.Authenticate(pairing.id, "my computer"); +AuthenticationRequest auth = api.Authenticate(pairing.id, "my computer"); // Once they've responded you can then check the status -AuthenticationStatus status = api.GetAuthenticationStatus(auth.id); -if (auth.pending == false && auth.granted == true) { +AuthenticationRequest status = api.GetAuthenticationRequest(auth.id); +if (status.pending == false && status.granted == true) { // Success! } ``` diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index effde06..64893bd 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -88,7 +88,7 @@ public Pairing GetPairing (string pairingRequestId) // // Provide pairing ID, a name for the terminal (displayed to user) and // an option action name (displayed to user) [defaults to "log in"] - public AuthenticationStatus Authenticate (string pairingId, string terminalName, string actionName = null, Dictionary extras = null) + public AuthenticationRequest Authenticate (string pairingId, string terminalName, string actionName = null, Dictionary extras = null) { string endpoint = "authentication_requests/initiate"; @@ -105,7 +105,7 @@ public AuthenticationStatus Authenticate (string pairingId, string terminalName, } var json = post (endpoint, parameters); - return new AuthenticationStatus (json); + return new AuthenticationRequest (json); } /// @@ -115,13 +115,13 @@ public AuthenticationStatus Authenticate (string pairingId, string terminalName, /// Unique terminal identifier for this terminal. Does not need to be human-readable. /// Name of the action to authenticate. default = "Login" /// Dictionary of arbitray key/value pairs to add to the webservice call - /// AuthenticationStatus object + /// AuthenticationRequest object /// Thrown when Toopher Authentication is disabled for the user /// Thrown when the user has no active pairings /// Thrown when the terminal cannot be identified /// Thrown when the user has deleted the pairing from their mobile device /// Thrown when there is a problem contacting the Toopher API - public AuthenticationStatus AuthenticateByUserName (string userName, string terminalIdentifier, string actionName = null, Dictionary extras = null) + public AuthenticationRequest AuthenticateByUserName (string userName, string terminalIdentifier, string actionName = null, Dictionary extras = null) { if (extras == null) { extras = new Dictionary (); @@ -136,20 +136,20 @@ public AuthenticationStatus AuthenticateByUserName (string userName, string term // // Provide authentication request ID returned when authentication request was // started. - public AuthenticationStatus GetAuthenticationStatus (string authenticationRequestId, string otp = null) + public AuthenticationRequest GetAuthenticationRequest (string authenticationRequestId, string otp = null) { string endpoint = String.Format ("authentication_requests/{0}", authenticationRequestId); JsonObject json; if (String.IsNullOrEmpty (otp)) { json = get (endpoint); - return new AuthenticationStatus (json); + return new AuthenticationRequest (json); } else { NameValueCollection parameters = new NameValueCollection (); parameters["otp"] = otp; json = post (endpoint + "/otp_auth", parameters); } - return new AuthenticationStatus (json); + return new AuthenticationRequest (json); } /// @@ -388,7 +388,7 @@ public Pairing (IDictionary _dict) } // Status information for an authentication request - public class AuthenticationStatus + public class AuthenticationRequest { private IDictionary _dict; public object this[string key] @@ -437,10 +437,10 @@ public string terminalName public override string ToString () { - return string.Format ("[AuthenticationStatus: id={0}; pending={1}; granted={2}; automated={3}; reason={4}; terminalId={5}; terminalName={6}]", id, pending, granted, automated, reason, terminalId, terminalName); + return string.Format ("[AuthenticationRequest: id={0}; pending={1}; granted={2}; automated={3}; reason={4}; terminalId={5}; terminalName={6}]", id, pending, granted, automated, reason, terminalId, terminalName); } - public AuthenticationStatus (IDictionary _dict) + public AuthenticationRequest (IDictionary _dict) { this._dict = _dict; try { diff --git a/ToopherDotNetDemo/ToopherDotNetDemo.cs b/ToopherDotNetDemo/ToopherDotNetDemo.cs index fd659f0..20bf7f5 100644 --- a/ToopherDotNetDemo/ToopherDotNetDemo.cs +++ b/ToopherDotNetDemo/ToopherDotNetDemo.cs @@ -109,9 +109,9 @@ public static void Main (string[] args) Console.ReadLine (); Console.WriteLine ("Checking status of authentication request..."); - AuthenticationStatus requestStatus; + AuthenticationRequest requestStatus; try { - requestStatus = api.GetAuthenticationStatus (requestId); + requestStatus = api.GetAuthenticationRequest (requestId); } catch (RequestError err) { Console.WriteLine (String.Format ("Could not check authentication status (reason:{0})", err.Message)); continue; diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index a14d06a..e803967 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -121,7 +121,7 @@ public void AuthenticateTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; - AuthenticationStatus auth = api.Authenticate("1", "test terminal"); + AuthenticationRequest auth = api.Authenticate("1", "test terminal"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["pairing_id"], "1"); Assert.AreEqual (WebClientMock.LastRequestData["terminal_name"], "test terminal"); @@ -132,7 +132,7 @@ public void AuthenticateByUserNameTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; - AuthenticationStatus auth = api.AuthenticateByUserName ("some other user", "random string", extras: new Dictionary() {{ "random_key" , "42" }}); + AuthenticationRequest auth = api.AuthenticateByUserName ("some other user", "random string", extras: new Dictionary() {{ "random_key" , "42" }}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some other user"); Assert.AreEqual (WebClientMock.LastRequestData["terminal_name_extra"], "random string"); @@ -159,7 +159,7 @@ public void ArbitraryParamtersOnAuthenticateTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; - AuthenticationStatus auth = api.Authenticate ("1", "test terminal", extras: new Dictionary () { { "test_param", "42" } }); + AuthenticationRequest auth = api.Authenticate ("1", "test terminal", extras: new Dictionary () { { "test_param", "42" } }); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); } @@ -179,11 +179,11 @@ public void AccessArbitraryKeysInPairingTest () } [Test] - public void AccessArbitraryKeysInAuthenticationStatusTest () + public void AccessArbitraryKeysInAuthenticationRequestTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}, ""random_key"":""84""}"; - AuthenticationStatus auth = api.GetAuthenticationStatus ("1"); + AuthenticationRequest auth = api.GetAuthenticationRequest ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (auth.id, "1"); Assert.IsFalse (auth.pending); @@ -245,11 +245,11 @@ public void UnauthorizedPairingRaisesCorrectErrorTest () } [Test] - public void GetAuthenticationStatusTest () + public void GetAuthenticationRequestTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; - AuthenticationStatus auth = api.GetAuthenticationStatus ("1"); + AuthenticationRequest auth = api.GetAuthenticationRequest ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (auth.id, "1"); Assert.IsFalse (auth.pending); @@ -260,11 +260,11 @@ public void GetAuthenticationStatusTest () } [Test] - public void GetAuthenticationStatusWIthOtpTest () + public void GetAuthenticationRequestWIthOtpTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; - AuthenticationStatus auth = api.GetAuthenticationStatus ("1", otp: "123456"); + AuthenticationRequest auth = api.GetAuthenticationRequest ("1", otp: "123456"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["otp"], "123456"); } diff --git a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs index 0ed51be..d35d0e3 100644 --- a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs +++ b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs @@ -67,8 +67,8 @@ public static void Main (string[] args) string terminalIdentifier = random.Next().ToString(); state = STATE.AUTHENTICATE; - AuthenticationStatus authStatus = null; Pairing pairing = null; + AuthenticationRequest authStatus = null; while (true) { switch (state) { @@ -108,7 +108,7 @@ public static void Main (string[] args) Console.WriteLine ("Checking status of authentication request..."); try { - authStatus = api.GetAuthenticationStatus (authStatus.id); + authStatus = api.GetAuthenticationRequest (authStatus.id); state = STATE.EVALUATE_AUTHENTICATION_STATUS; } catch (RequestError err) { Console.WriteLine (String.Format ("Could not check authentication status (reason:{0})", err.Message)); @@ -139,7 +139,7 @@ public static void Main (string[] args) case STATE.ENTER_OTP: { Console.Write ("Please enter the Pairing OTP value generated in the Toopher Mobile App: "); string otp = Console.ReadLine ().Trim (); - authStatus = api.GetAuthenticationStatus (authStatus.id, otp: otp); + authStatus = api.GetAuthenticationRequest (authStatus.id, otp: otp); state = STATE.EVALUATE_AUTHENTICATION_STATUS; break; }; From e4492ac474021fb43159934d8aac266b296ceac3 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Mon, 9 Feb 2015 15:58:19 -0600 Subject: [PATCH 003/103] Update ToopherApi.Pair to handle regular pairing, QR pairing and SMS pairing --- ToopherDotNet/ToopherDotNet.cs | 25 ++++++++++++++++------ ToopherDotNetTests/ToopherDotNetTests.cs | 27 ++++++++++++++++++++++-- 2 files changed, 44 insertions(+), 8 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 64893bd..573f698 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -8,6 +8,7 @@ using SimpleJson; using System.IO; using System.Linq; +using System.Text.RegularExpressions; namespace Toopher { @@ -51,18 +52,30 @@ public ToopherAPI (string consumerKey, string consumerSecret, string baseUrl = n } } - // Pair your requester with a user's Toopher application + // Create an SMS pairing, QR pairing or regular pairing // - // Must provide a pairing request (generated on their phone) and - // the user's name - public Pairing Pair (string pairingPhrase, string userName, Dictionary extras = null) + // Must provide a username and phone number for an SMS pairing + // Must provide a username for a QR pairing + // Must provide a username and pairing phrase for a regular pairing + public Pairing Pair (string userName, string pairingPhraseOrNum = null, Dictionary extras = null) { - string endpoint = "pairings/create"; + string endpoint; NameValueCollection parameters = new NameValueCollection (); - parameters.Add ("pairing_phrase", pairingPhrase); parameters.Add ("user_name", userName); + if (pairingPhraseOrNum != null) { + if (Regex.IsMatch(pairingPhraseOrNum, @"[0-9]")) { + parameters.Add("phone_number", pairingPhraseOrNum); + endpoint = "pairings/create/sms"; + } else { + parameters.Add ("pairing_phrase", pairingPhraseOrNum); + endpoint = "pairings/create"; + } + } else { + endpoint = "pairings/create/qr"; + } + if (extras != null) { foreach (KeyValuePair kvp in extras) { parameters.Add (kvp.Key, kvp.Value); diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index e803967..fe8ea8a 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -105,16 +105,39 @@ public void ToopherBaseUrlTest () } [Test] - public void CreatePairingTest () + public void CreatePairingWithPhraseTest () { var api = getApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; - Pairing pairing = api.Pair ("awkward turtle", "some user"); + Pairing pairing = api.Pair ("some user", "awkward turtle"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["pairing_phrase"], "awkward turtle"); + Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some user"); Assert.AreEqual (pairing.id, "1"); } + [Test] + public void CreateSmsPairingTest () + { + var api = getApi(); + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; + Pairing pairing = api.Pair ("some user", "555-555-5555"); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (WebClientMock.LastRequestData["phone_number"], "555-555-5555"); + Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some user"); + Assert.AreEqual (pairing.id, "1"); + } + + [Test] + public void CreateQrPairingTest () + { + var api = getApi(); + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; + Pairing pairing = api.Pair ("some user"); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some user"); + Assert.AreEqual (pairing.id, "1"); + } [Test] public void AuthenticateTest () From 3ee9249fd52036a60dc26a0bd3cbab4b9f7964bb Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Mon, 9 Feb 2015 17:10:59 -0600 Subject: [PATCH 004/103] Update ToopherAPI.Authenticate to accept pairingId/terminalName or username/terminalNameExtra --- ToopherDotNet/ToopherDotNet.cs | 49 +++++++------------ ToopherDotNetTests/ToopherDotNetTests.cs | 30 +++++------- .../ToopherZeroStorageDemo.cs | 2 +- 3 files changed, 31 insertions(+), 50 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 573f698..231c7a0 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -97,20 +97,29 @@ public Pairing GetPairing (string pairingRequestId) return new Pairing (json); } - // Authenticate an action with Toopher + // Initiate an authentication request by pairing id or username // - // Provide pairing ID, a name for the terminal (displayed to user) and - // an option action name (displayed to user) [defaults to "log in"] - public AuthenticationRequest Authenticate (string pairingId, string terminalName, string actionName = null, Dictionary extras = null) + // Provide pairing ID or username, a name for the terminal (displayed to user) or requester-specified ID, + // an optional action name (displayed to user) [defaults to "log in"] and + // an optional Dictionary of extras to be sent to the API + public AuthenticationRequest Authenticate (string pairingIdOrUsername, string terminalNameOrTerminalNameExtra, string actionName = null, Dictionary extras = null) { string endpoint = "authentication_requests/initiate"; - NameValueCollection parameters = new NameValueCollection (); - parameters.Add ("pairing_id", pairingId); - parameters.Add ("terminal_name", terminalName); - if (actionName != null) { + + try { + Guid pairingId = new Guid(pairingIdOrUsername); + parameters.Add ("pairing_id", pairingIdOrUsername); + parameters.Add ("terminal_name", terminalNameOrTerminalNameExtra); + } catch (Exception e) { + parameters.Add ("user_name", pairingIdOrUsername); + parameters.Add ("terminal_name_extra", terminalNameOrTerminalNameExtra); + } + + if (actionName != null && actionName.Length > 0) { parameters.Add ("action_name", actionName); } + if (extras != null) { foreach (KeyValuePair kvp in extras) { parameters.Add (kvp.Key, kvp.Value); @@ -121,30 +130,6 @@ public AuthenticationRequest Authenticate (string pairingId, string terminalName return new AuthenticationRequest (json); } - /// - /// Authenticate an action with Toopher - /// - /// Name of the user - /// Unique terminal identifier for this terminal. Does not need to be human-readable. - /// Name of the action to authenticate. default = "Login" - /// Dictionary of arbitray key/value pairs to add to the webservice call - /// AuthenticationRequest object - /// Thrown when Toopher Authentication is disabled for the user - /// Thrown when the user has no active pairings - /// Thrown when the terminal cannot be identified - /// Thrown when the user has deleted the pairing from their mobile device - /// Thrown when there is a problem contacting the Toopher API - public AuthenticationRequest AuthenticateByUserName (string userName, string terminalIdentifier, string actionName = null, Dictionary extras = null) - { - if (extras == null) { - extras = new Dictionary (); - } - extras["user_name"] = userName; - extras["terminal_name_extra"] = terminalIdentifier; - - return this.Authenticate (null, null, actionName, extras); - } - // Check on status of authentication request // // Provide authentication request ID returned when authentication request was diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index fe8ea8a..3b01786 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -140,33 +140,29 @@ public void CreateQrPairingTest () } [Test] - public void AuthenticateTest () + public void AuthenticateWithPairingIdTest () { + string pairingId = Guid.NewGuid().ToString(); var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; - AuthenticationRequest auth = api.Authenticate("1", "test terminal"); + WebClientMock.ReturnValue = @"{""id"":"" + pairingId + "", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; + AuthenticationRequest auth = api.Authenticate(pairingId, "test terminal"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["pairing_id"], "1"); + Assert.AreEqual (WebClientMock.LastRequestData["pairing_id"], pairingId); Assert.AreEqual (WebClientMock.LastRequestData["terminal_name"], "test terminal"); } [Test] - public void AuthenticateByUserNameTest () + public void AuthenticateWithUsernameAndExtrasTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; - AuthenticationRequest auth = api.AuthenticateByUserName ("some other user", "random string", extras: new Dictionary() {{ "random_key" , "42" }}); + AuthenticationRequest auth = api.Authenticate ("some other user", "requester specified id", extras: new Dictionary() {{ "random_key" , "42" }}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some other user"); - Assert.AreEqual (WebClientMock.LastRequestData["terminal_name_extra"], "random string"); + Assert.AreEqual (WebClientMock.LastRequestData["terminal_name_extra"], "requester specified id"); Assert.AreEqual (WebClientMock.LastRequestData["random_key"], "42"); - Assert.AreEqual (WebClientMock.LastRequestData["terminal_name"], ""); } - - - - [Test] public void ArbitraryParametersOnPairTest () { @@ -224,7 +220,7 @@ public void DisabledUserRaisesCorrectErrorTest () var api = getApi (); WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":704, ""error_message"":""The specified user has disabled Toopher authentication.""}"); - api.AuthenticateByUserName ("some disabled user", "some random string"); + api.Authenticate ("some disabled user", "some random string"); } [Test] @@ -234,7 +230,7 @@ public void UnknownUserRaisesCorrectErrorTest () var api = getApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":705, ""error_message"":""No matching user exists.""}"); - api.AuthenticateByUserName ("some unknown user", "some random string"); + api.Authenticate ("some unknown user", "some random string"); } [Test] @@ -244,7 +240,7 @@ public void UnknownTerminalRaisesCorrectErrorTest () var api = getApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":706, ""error_message"":""No matching terminal exists.""}"); - api.AuthenticateByUserName ("some unknown user", "some random string"); + api.Authenticate ("some unknown user", "some random string"); } [Test] @@ -254,7 +250,7 @@ public void DeactivatedPairingRaisesCorrectErrorTest () var api = getApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":601, ""error_message"":""This pairing has been deactivated.""}"); - api.AuthenticateByUserName ("some disabled user", "some random string"); + api.Authenticate ("some disabled user", "some random string"); } [Test] @@ -264,7 +260,7 @@ public void UnauthorizedPairingRaisesCorrectErrorTest () var api = getApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":601, ""error_message"":""This pairing has not been authorized to authenticate.""}"); - api.AuthenticateByUserName ("some unauthorized user", "some random string"); + api.Authenticate ("some unauthorized user", "some random string"); } [Test] diff --git a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs index d35d0e3..3cf002d 100644 --- a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs +++ b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs @@ -75,7 +75,7 @@ public static void Main (string[] args) case STATE.AUTHENTICATE: { try { Console.WriteLine (String.Format ("\n\nAuthenticating user \"{0}\"", userName)); - authStatus = api.AuthenticateByUserName (userName, terminalIdentifier); + authStatus = api.Authenticate (userName, terminalIdentifier); state = STATE.EVALUATE_AUTHENTICATION_STATUS; } catch (UserDisabledError e) { state = STATE.USER_DISABLED; From 7a7405bf548d4d2b1a56d1056188214e0c0aef50 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 10 Feb 2015 09:15:02 -0600 Subject: [PATCH 005/103] Update language library version to 2.0.0 --- ToopherDotNet/ToopherDotNet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 231c7a0..a871ecb 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -17,7 +17,7 @@ public class ToopherAPI - public const string VERSION = "1.1.0"; + public const string VERSION = "2.0.0"; public const string DEFAULT_BASE_URL = "https://api.toopher.com/v1/"; string consumerKey; From 900548343759ccbb09d58c211cbe4143a743cf23 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 10 Feb 2015 17:04:17 -0600 Subject: [PATCH 006/103] Add ToopherAPI.AdvancedApiUsageFactory --- ToopherDotNet/ToopherDotNet.cs | 14 ++++++++++++-- ToopherDotNetTests/ToopherDotNetTests.cs | 7 +++++++ 2 files changed, 19 insertions(+), 2 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index a871ecb..86a5f99 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -15,10 +15,9 @@ namespace Toopher public class ToopherAPI { - - public const string VERSION = "2.0.0"; public const string DEFAULT_BASE_URL = "https://api.toopher.com/v1/"; + public ToopherAPI.AdvancedApiUsageFactory advanced; string consumerKey; string consumerSecret; @@ -38,6 +37,7 @@ public class ToopherAPI /// Override WebClient class for testing purposes public ToopherAPI (string consumerKey, string consumerSecret, string baseUrl = null, Type webClientProxyType = null) { + this.advanced = new ToopherAPI.AdvancedApiUsageFactory(this); this.consumerKey = consumerKey; this.consumerSecret = consumerSecret; if (baseUrl != null) { @@ -319,10 +319,20 @@ private void parseRequestError (JsonObject err) throw new RequestError (errMessage); } } + } + + public class AdvancedApiUsageFactory + { + private ToopherAPI api; + public AdvancedApiUsageFactory(ToopherAPI toopherApi) + { + this.api = toopherApi; + } } } + // Status information for a pairing request public class Pairing { diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 3b01786..6e74add 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -310,5 +310,12 @@ public void GeneratePairingLinkTest () string pairingResetLink = api.GetPairingResetLink ("1"); Assert.AreEqual (pairingResetLink, "http://testonly/pairings/1/reset?reset_authorization=abcde"); } + + [Test] + public void GenerateAdvancedApiUsageFactory () + { + var api = getApi(); + Assert.IsInstanceOf (api.advanced); + } } } From 55d4e537bab0c927ffa8ab632c4e4a90b8154abd Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 10 Feb 2015 17:06:27 -0600 Subject: [PATCH 007/103] Try to create new Guid without assigning to var --- ToopherDotNet/ToopherDotNet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 86a5f99..d1a02b8 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -108,7 +108,7 @@ public AuthenticationRequest Authenticate (string pairingIdOrUsername, string te NameValueCollection parameters = new NameValueCollection (); try { - Guid pairingId = new Guid(pairingIdOrUsername); + new Guid(pairingIdOrUsername); parameters.Add ("pairing_id", pairingIdOrUsername); parameters.Add ("terminal_name", terminalNameOrTerminalNameExtra); } catch (Exception e) { From 023e8d62d59c340b34775576c794c5afd0fbec0f Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 10 Feb 2015 17:58:52 -0600 Subject: [PATCH 008/103] Clean up and add tests --- ToopherDotNetTests/ToopherDotNetTests.cs | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 6e74add..a7aea5e 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -87,7 +87,6 @@ private ToopherAPI getApi () [Test()] public void ToopherVersionTest () { - Assert.IsTrue(ToopherAPI.VERSION is string); string[] strs = ToopherAPI.VERSION.Split('.'); int major = int.Parse(strs[0]); int minor = int.Parse(strs[1]); @@ -100,7 +99,7 @@ public void ToopherVersionTest () [Test()] public void ToopherBaseUrlTest () { - Assert.IsTrue(ToopherAPI.DEFAULT_BASE_URL is string); + StringAssert.Contains ("https", ToopherAPI.DEFAULT_BASE_URL); Assert.IsTrue(System.Uri.IsWellFormedUriString(ToopherAPI.DEFAULT_BASE_URL, System.UriKind.Absolute)); } @@ -142,13 +141,14 @@ public void CreateQrPairingTest () [Test] public void AuthenticateWithPairingIdTest () { - string pairingId = Guid.NewGuid().ToString(); var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":"" + pairingId + "", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; + string pairingId = Guid.NewGuid().ToString(); + WebClientMock.ReturnValue = @"{""id"":""" + pairingId + @""", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; AuthenticationRequest auth = api.Authenticate(pairingId, "test terminal"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["pairing_id"], pairingId); Assert.AreEqual (WebClientMock.LastRequestData["terminal_name"], "test terminal"); + Assert.AreEqual (auth.id, pairingId); } [Test] @@ -161,6 +161,7 @@ public void AuthenticateWithUsernameAndExtrasTest () Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some other user"); Assert.AreEqual (WebClientMock.LastRequestData["terminal_name_extra"], "requester specified id"); Assert.AreEqual (WebClientMock.LastRequestData["random_key"], "42"); + Assert.AreEqual (auth.id, "1"); } [Test] @@ -171,6 +172,7 @@ public void ArbitraryParametersOnPairTest () Pairing pairing = api.Pair ("awkward turtle", "some user", extras: new Dictionary(){{"test_param", "42"}}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); + Assert.AreEqual (pairing.id, "1"); } [Test] @@ -181,6 +183,7 @@ public void ArbitraryParamtersOnAuthenticateTest () AuthenticationRequest auth = api.Authenticate ("1", "test terminal", extras: new Dictionary () { { "test_param", "42" } }); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); + Assert.AreEqual (auth.id, "1"); } [Test] @@ -286,6 +289,7 @@ public void GetAuthenticationRequestWIthOtpTest () AuthenticationRequest auth = api.GetAuthenticationRequest ("1", otp: "123456"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["otp"], "123456"); + Assert.AreEqual (auth.id, "1"); } From 39616bb2a82dd87d73744d64c559ddcb074f0c9f Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 10 Feb 2015 17:59:56 -0600 Subject: [PATCH 009/103] Clean up ToopherAPI.Authenticate --- ToopherDotNet/ToopherDotNet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index d1a02b8..a5b563b 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -111,7 +111,7 @@ public AuthenticationRequest Authenticate (string pairingIdOrUsername, string te new Guid(pairingIdOrUsername); parameters.Add ("pairing_id", pairingIdOrUsername); parameters.Add ("terminal_name", terminalNameOrTerminalNameExtra); - } catch (Exception e) { + } catch (Exception) { parameters.Add ("user_name", pairingIdOrUsername); parameters.Add ("terminal_name_extra", terminalNameOrTerminalNameExtra); } From 522d363c4efce6b247752c3302d9be9f10f406f3 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 09:51:46 -0600 Subject: [PATCH 010/103] Rename ToopherAPI to ToopherApi --- CONTRIBUTING.md | 2 +- ToopherDotNet/ToopherDotNet.cs | 22 +++++++++---------- ToopherDotNetDemo/ToopherDotNetDemo.cs | 4 ++-- ToopherDotNetTests/ToopherDotNetTests.cs | 12 +++++----- .../ToopherZeroStorageDemo.cs | 4 ++-- 5 files changed, 22 insertions(+), 22 deletions(-) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index cc4bcbe..aa4d328 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -1,4 +1,4 @@ -# ToopherAPI DotNet Client +# ToopherApi DotNet Client #### .NET Framework Version >=4.5 diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index a5b563b..8c30dcc 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -12,38 +12,38 @@ namespace Toopher { - public class ToopherAPI + public class ToopherApi { public const string VERSION = "2.0.0"; public const string DEFAULT_BASE_URL = "https://api.toopher.com/v1/"; - public ToopherAPI.AdvancedApiUsageFactory advanced; + public ToopherApi.AdvancedApiUsageFactory advanced; string consumerKey; string consumerSecret; string baseUrl; Type webClientProxyType; - // Create the ToopherAPI object tied to your requester credentials - // + // Create the ToopherApi object tied to your requester credentials + // // Credentials are available on https://dev.toopher.com /// - /// Create a new instance of the ToopherAPI client tied to your requester + /// Create a new instance of the ToopherApi client tied to your requester /// credentials. Credentials are available at https://dev.toopher.com /// /// OAuth Consumer Key /// OAuth Consumer Secret - /// Override url for ToopherAPI webservice (default=https://api.toopher.com/v1/) + /// Override url for ToopherApi webservice (default=https://api.toopher.com/v1/) /// Override WebClient class for testing purposes - public ToopherAPI (string consumerKey, string consumerSecret, string baseUrl = null, Type webClientProxyType = null) + public ToopherApi (string consumerKey, string consumerSecret, string baseUrl = null, Type webClientProxyType = null) { - this.advanced = new ToopherAPI.AdvancedApiUsageFactory(this); + this.advanced = new ToopherApi.AdvancedApiUsageFactory(this); this.consumerKey = consumerKey; this.consumerSecret = consumerSecret; if (baseUrl != null) { this.baseUrl = baseUrl; } else { - this.baseUrl = ToopherAPI.DEFAULT_BASE_URL; + this.baseUrl = ToopherApi.DEFAULT_BASE_URL; } if (webClientProxyType != null) { this.webClientProxyType = webClientProxyType; @@ -323,9 +323,9 @@ private void parseRequestError (JsonObject err) public class AdvancedApiUsageFactory { - private ToopherAPI api; + private ToopherApi api; - public AdvancedApiUsageFactory(ToopherAPI toopherApi) + public AdvancedApiUsageFactory(ToopherApi toopherApi) { this.api = toopherApi; } diff --git a/ToopherDotNetDemo/ToopherDotNetDemo.cs b/ToopherDotNetDemo/ToopherDotNetDemo.cs index 20bf7f5..e4af8cf 100644 --- a/ToopherDotNetDemo/ToopherDotNetDemo.cs +++ b/ToopherDotNetDemo/ToopherDotNetDemo.cs @@ -8,7 +8,7 @@ class ToopherDotNetDemo public const string DEFAULT_USERNAME = "demo@toopher.com"; public const string DEFAULT_TERMINAL_NAME = "my computer"; - public static ToopherAPI api; + public static ToopherApi api; public static void Main (string[] args) { @@ -30,7 +30,7 @@ public static void Main (string[] args) } string baseUrl = System.Environment.GetEnvironmentVariable ("TOOPHER_BASE_URL"); - api = new Toopher.ToopherAPI (consumerKey, consumerSecret, baseUrl); + api = new Toopher.ToopherApi (consumerKey, consumerSecret, baseUrl); string pairingId; while (true) { diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index a7aea5e..aa333c6 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -79,15 +79,15 @@ public void Init () WebClientMock.LastRequestData = null; } - private ToopherAPI getApi () + private ToopherApi getApi () { - return new ToopherAPI ("key", "secret", null, typeof (WebClientMock)); + return new ToopherApi ("key", "secret", null, typeof (WebClientMock)); } [Test()] public void ToopherVersionTest () { - string[] strs = ToopherAPI.VERSION.Split('.'); + string[] strs = ToopherApi.VERSION.Split('.'); int major = int.Parse(strs[0]); int minor = int.Parse(strs[1]); int patchLevel = int.Parse(strs[2]); @@ -99,8 +99,8 @@ public void ToopherVersionTest () [Test()] public void ToopherBaseUrlTest () { - StringAssert.Contains ("https", ToopherAPI.DEFAULT_BASE_URL); - Assert.IsTrue(System.Uri.IsWellFormedUriString(ToopherAPI.DEFAULT_BASE_URL, System.UriKind.Absolute)); + StringAssert.Contains ("https", ToopherApi.DEFAULT_BASE_URL); + Assert.IsTrue(System.Uri.IsWellFormedUriString(ToopherApi.DEFAULT_BASE_URL, System.UriKind.Absolute)); } [Test] @@ -319,7 +319,7 @@ public void GeneratePairingLinkTest () public void GenerateAdvancedApiUsageFactory () { var api = getApi(); - Assert.IsInstanceOf (api.advanced); + Assert.IsInstanceOf (api.advanced); } } } diff --git a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs index 3cf002d..055f4a3 100644 --- a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs +++ b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs @@ -9,7 +9,7 @@ class ToopherZeroStorageDemo public const string DEFAULT_USERNAME = "demo@toopher.com"; public const string DEFAULT_TERMINAL_NAME = "my computer"; - public static ToopherAPI api; + public static ToopherApi api; static Random random = new Random (DateTime.Now.Millisecond); enum STATE @@ -57,7 +57,7 @@ public static void Main (string[] args) } string baseUrl = System.Environment.GetEnvironmentVariable ("TOOPHER_BASE_URL"); - api = new Toopher.ToopherAPI (consumerKey, consumerSecret, baseUrl); + api = new Toopher.ToopherApi (consumerKey, consumerSecret, baseUrl); Console.Write(String.Format("Enter a username to authenticate with Toopher [{0}]: ", DEFAULT_USERNAME)); string userName = Console.ReadLine (); From 7462aa4a584bb1a6e843d12fc81512feb7d78c9b Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 10:00:33 -0600 Subject: [PATCH 011/103] Add ToopherApi.AdvancedApiUsageFactory.Pairings --- ToopherDotNet/ToopherDotNet.cs | 19 ++++++++++++++++--- ToopherDotNetTests/ToopherDotNetTests.cs | 7 +++++++ 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 8c30dcc..3af8608 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -323,12 +323,25 @@ private void parseRequestError (JsonObject err) public class AdvancedApiUsageFactory { - private ToopherApi api; + public ToopherApi.AdvancedApiUsageFactory.Pairings pairings; - public AdvancedApiUsageFactory(ToopherApi toopherApi) + public AdvancedApiUsageFactory (ToopherApi toopherApi) { - this.api = toopherApi; + this.pairings = new ToopherApi.AdvancedApiUsageFactory.Pairings(toopherApi); } + + public class Pairings + { + private ToopherApi api; + + public Pairings (ToopherApi toopherApi) + { + this.api = toopherApi; + } + + } + + } } diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index aa333c6..4cbfa25 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -321,5 +321,12 @@ public void GenerateAdvancedApiUsageFactory () var api = getApi(); Assert.IsInstanceOf (api.advanced); } + + [Test] + public void GenerateAdvancedPairings () + { + var api = getApi(); + Assert.IsInstanceOf (api.advanced.pairings); + } } } From c055a9402536f9f6e71352faf9ca72f10a4acd42 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 10:21:35 -0600 Subject: [PATCH 012/103] Add ToopherApi.AdvancedApiUsageFactory.Pairings.GetById --- ToopherDotNet/ToopherDotNet.cs | 8 ++++++-- ToopherDotNetDemo/ToopherDotNetDemo.cs | 2 +- ToopherDotNetTests/ToopherDotNetTests.cs | 9 +++++---- ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs | 2 +- 4 files changed, 13 insertions(+), 8 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 3af8608..91358a5 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -339,9 +339,13 @@ public Pairings (ToopherApi toopherApi) this.api = toopherApi; } + public Pairing GetById (string pairingId) + { + string endpoint = string.Format ("pairings/{0}", pairingId); + var json = api.get (endpoint); + return new Pairing (json); + } } - - } } diff --git a/ToopherDotNetDemo/ToopherDotNetDemo.cs b/ToopherDotNetDemo/ToopherDotNetDemo.cs index e4af8cf..3960d81 100644 --- a/ToopherDotNetDemo/ToopherDotNetDemo.cs +++ b/ToopherDotNetDemo/ToopherDotNetDemo.cs @@ -72,7 +72,7 @@ public static void Main (string[] args) Console.WriteLine ("Checking status of pairing request..."); try { - var pairing = api.GetPairing (pairingId); + var pairing = api.advanced.pairings.GetById (pairingId); if (pairing.enabled) { Console.WriteLine ("Pairing complete"); break; diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 4cbfa25..58ea363 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -187,11 +187,11 @@ public void ArbitraryParamtersOnAuthenticateTest () } [Test] - public void AccessArbitraryKeysInPairingTest () + public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}, ""random_key"":""84""}"; - Pairing pairing = api.GetPairing ("1"); + Pairing pairing = api.advanced.pairings.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (pairing.id, "1"); Assert.AreEqual (pairing.userName, "some user"); @@ -294,11 +294,12 @@ public void GetAuthenticationRequestWIthOtpTest () [Test] - public void PairingTest () + public void AdvancedPairingsGetByIdTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; - Pairing pairing = api.GetPairing ("1"); + Pairing pairing = api.advanced.pairings.GetById ("1"); + Assert.IsInstanceOf (pairing); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (pairing.id, "1"); Assert.AreEqual (pairing.userName, "some user"); diff --git a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs index 055f4a3..7edd988 100644 --- a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs +++ b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs @@ -176,7 +176,7 @@ public static void Main (string[] args) break; }; case STATE.POLL_FOR_PAIRING: { - pairing = api.GetPairing(pairing.id); + pairing = api.advanced.pairings.GetById (pairing.id); if (pairing.enabled) { Console.WriteLine ("Pairing complete"); state = STATE.AUTHENTICATE; From 6299f3cfb1d6da06740bda52edd176e5f548c518 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 10:28:48 -0600 Subject: [PATCH 013/103] Remove old GetPairing and GetAuthenticationRequest --- ToopherDotNet/ToopherDotNet.cs | 31 ------------------------------- 1 file changed, 31 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 91358a5..79860a7 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -86,17 +86,6 @@ public Pairing Pair (string userName, string pairingPhraseOrNum = null, Dictiona return new Pairing (json); } - // Check on status of a pairing request - // - // Must provide the ID returned when the pairing request was initiated - public Pairing GetPairing (string pairingRequestId) - { - string endpoint = String.Format ("pairings/{0}", pairingRequestId); - - var json = get (endpoint); - return new Pairing (json); - } - // Initiate an authentication request by pairing id or username // // Provide pairing ID or username, a name for the terminal (displayed to user) or requester-specified ID, @@ -130,26 +119,6 @@ public AuthenticationRequest Authenticate (string pairingIdOrUsername, string te return new AuthenticationRequest (json); } - // Check on status of authentication request - // - // Provide authentication request ID returned when authentication request was - // started. - public AuthenticationRequest GetAuthenticationRequest (string authenticationRequestId, string otp = null) - { - string endpoint = String.Format ("authentication_requests/{0}", authenticationRequestId); - - JsonObject json; - if (String.IsNullOrEmpty (otp)) { - json = get (endpoint); - return new AuthenticationRequest (json); - } else { - NameValueCollection parameters = new NameValueCollection (); - parameters["otp"] = otp; - json = post (endpoint + "/otp_auth", parameters); - } - return new AuthenticationRequest (json); - } - /// /// Create a named terminal in the Toopher API for the (userName, terminalIdentifier) combination /// From 6ba790e90e87a33836131cfcadb02f741334669b Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 11:40:25 -0600 Subject: [PATCH 014/103] Move get(), post(), request() to ToopherApi.AdvancedApiUsageFactory.ApiRawRequester --- ToopherDotNet/ToopherDotNet.cs | 241 ++++++++++++----------- ToopherDotNetTests/ToopherDotNetTests.cs | 7 + 2 files changed, 133 insertions(+), 115 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 79860a7..af8d8d0 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -82,7 +82,7 @@ public Pairing Pair (string userName, string pairingPhraseOrNum = null, Dictiona } } - var json = post (endpoint, parameters); + var json = advanced.raw.post (endpoint, parameters); return new Pairing (json); } @@ -115,7 +115,7 @@ public AuthenticationRequest Authenticate (string pairingIdOrUsername, string te } } - var json = post (endpoint, parameters); + var json = advanced.raw.post (endpoint, parameters); return new AuthenticationRequest (json); } @@ -133,7 +133,7 @@ public void CreateUserTerminal (string userName, string terminalName, string ter parameters["user_name"] = userName; parameters["name"] = terminalName; parameters["name_extra"] = terminalIdentifier; - post (endpoint, parameters); + advanced.raw.post (endpoint, parameters); } /// @@ -150,7 +150,7 @@ public void SetToopherEnabledForUser (string userName, bool toopherEnabled) NameValueCollection parameters = new NameValueCollection (); parameters["user_name"] = userName; - JsonArray jArr = getArray (searchEndpoint, parameters); + JsonArray jArr = advanced.raw.getArray (searchEndpoint, parameters); if (jArr.Count > 1) { throw new RequestError ("Multiple users with name = " + userName); } @@ -163,7 +163,7 @@ public void SetToopherEnabledForUser (string userName, bool toopherEnabled) string updateEndpoint = "users/" + userId; parameters = new NameValueCollection (); parameters["disable_toopher_auth"] = toopherEnabled ? "false" : "true"; - post (updateEndpoint, parameters); + advanced.raw.post (updateEndpoint, parameters); } public string GetPairingResetLink (string pairingId, string securityQuestion = null, string securityAnswer = null) @@ -173,146 +173,157 @@ public string GetPairingResetLink (string pairingId, string securityQuestion = n parameters["security_question"] = securityQuestion; parameters["security_answer"] = securityAnswer; - JsonObject pairingResetLink = post (endpoint, parameters); + JsonObject pairingResetLink = advanced.raw.post (endpoint, parameters); return (string)pairingResetLink["url"]; } - private object request (string method, string endpoint, NameValueCollection parameters = null) + public class AdvancedApiUsageFactory { - // Normalize method string - method = method.ToUpper (); + public ToopherApi.AdvancedApiUsageFactory.Pairings pairings; + public ToopherApi.AdvancedApiUsageFactory.ApiRawRequester raw; - // Build an empty collection for parameters (if necessary) - if (parameters == null) { - parameters = new NameValueCollection (); + public AdvancedApiUsageFactory (ToopherApi toopherApi) + { + this.pairings = new ToopherApi.AdvancedApiUsageFactory.Pairings(toopherApi); + this.raw = new ToopherApi.AdvancedApiUsageFactory.ApiRawRequester(toopherApi); } - // can't have null parameters, or oauth signing will barf - foreach (String key in parameters.AllKeys){ - if (parameters[key] == null) { - parameters[key] = ""; + public class Pairings + { + private ToopherApi api; + + public Pairings (ToopherApi toopherApi) + { + this.api = toopherApi; } - } - var client = OAuthRequest.ForRequestToken (this.consumerKey, this.consumerSecret); - client.RequestUrl = this.baseUrl + endpoint; - client.Method = method; + public Pairing GetById (string pairingId) + { + string endpoint = string.Format ("pairings/{0}", pairingId); + var json = api.advanced.raw.get (endpoint); + return new Pairing (json); + } + } - string auth = client.GetAuthorizationHeader (parameters); - // FIXME: OAuth library puts extraneous comma at end, workaround: remove it if present - auth = auth.TrimEnd (new char[] { ',' }); + public class ApiRawRequester + { + private ToopherApi api; - using (WebClientProxy wClient = (WebClientProxy)Activator.CreateInstance(webClientProxyType)) { - wClient.Headers.Add ("Authorization", auth); - wClient.Headers.Add ("User-Agent", - string.Format ("Toopher-DotNet/{0} (DotNet {1})", VERSION, Environment.Version.ToString ())); - if (parameters.Count > 0) { - wClient.QueryString = parameters; + public ApiRawRequester (ToopherApi toopherApi) + { + this.api = toopherApi; } - string response; - try { - if (method.Equals ("POST")) { - var responseArray = wClient.UploadValues (client.RequestUrl, client.Method, parameters); - response = Encoding.UTF8.GetString (responseArray); - } else { - response = wClient.DownloadString (client.RequestUrl); - } - } catch (WebException wex) { - IHttpWebResponse httpResp = HttpWebResponseWrapper.create(wex.Response); - string error_message; - using (Stream stream = httpResp.GetResponseStream ()) { - StreamReader reader = new StreamReader (stream, Encoding.UTF8); - error_message = reader.ReadToEnd (); - } - - String statusLine = httpResp.StatusCode.ToString () + " : " + httpResp.StatusDescription; + private object request (string method, string endpoint, NameValueCollection parameters = null) + { + // Normalize method string + method = method.ToUpper (); - if (String.IsNullOrEmpty (error_message)) { - throw new RequestError (statusLine); - } else { + // Build an empty collection for parameters (if necessary) + if (parameters == null) { + parameters = new NameValueCollection (); + } - try { - // Attempt to parse JSON response - var json = (JsonObject)SimpleJson.SimpleJson.DeserializeObject (error_message); - parseRequestError (json); - } catch (RequestError e) { - throw e; - } catch (Exception) { - throw new RequestError (statusLine + " : " + error_message); + // can't have null parameters, or oauth signing will barf + foreach (String key in parameters.AllKeys){ + if (parameters[key] == null) { + parameters[key] = ""; } } - throw new RequestError (error_message, wex); - } - - try { - return SimpleJson.SimpleJson.DeserializeObject (response); - } catch (Exception ex) { - throw new RequestError ("Could not parse response", ex); - } - } + var client = OAuthRequest.ForRequestToken (api.consumerKey, api.consumerSecret); + client.RequestUrl = api.baseUrl + endpoint; + client.Method = method; - } + string auth = client.GetAuthorizationHeader (parameters); + // FIXME: OAuth library puts extraneous comma at end, workaround: remove it if present + auth = auth.TrimEnd (new char[] { ',' }); - - private JsonObject get (string endpoint, NameValueCollection parameters = null) - { - return (JsonObject)request ("GET", endpoint, parameters); - } - private JsonArray getArray (string endpoint, NameValueCollection parameters = null) - { - return (JsonArray)request ("GET", endpoint, parameters); - } + using (WebClientProxy wClient = (WebClientProxy)Activator.CreateInstance(api.webClientProxyType)) { + wClient.Headers.Add ("Authorization", auth); + wClient.Headers.Add ("User-Agent", + string.Format ("Toopher-DotNet/{0} (DotNet {1})", VERSION, Environment.Version.ToString ())); + if (parameters.Count > 0) { + wClient.QueryString = parameters; + } - private JsonObject post (string endpoint, NameValueCollection parameters = null) - { - return (JsonObject) request ("POST", endpoint, parameters); - } + string response; + try { + if (method.Equals ("POST")) { + var responseArray = wClient.UploadValues (client.RequestUrl, client.Method, parameters); + response = Encoding.UTF8.GetString (responseArray); + } else { + response = wClient.DownloadString (client.RequestUrl); + } + } catch (WebException wex) { + IHttpWebResponse httpResp = HttpWebResponseWrapper.create(wex.Response); + string error_message; + using (Stream stream = httpResp.GetResponseStream ()) { + StreamReader reader = new StreamReader (stream, Encoding.UTF8); + error_message = reader.ReadToEnd (); + } + + String statusLine = httpResp.StatusCode.ToString () + " : " + httpResp.StatusDescription; + + if (String.IsNullOrEmpty (error_message)) { + throw new RequestError (statusLine); + } else { + + try { + // Attempt to parse JSON response + var json = (JsonObject)SimpleJson.SimpleJson.DeserializeObject (error_message); + parseRequestError (json); + } catch (RequestError e) { + throw e; + } catch (Exception) { + throw new RequestError (statusLine + " : " + error_message); + } + } + + throw new RequestError (error_message, wex); + } - private void parseRequestError (JsonObject err) - { - long errCode = (long)err["error_code"]; - string errMessage = (string)err["error_message"]; - if (errCode == UserDisabledError.ERROR_CODE) { - throw new UserDisabledError (); - } else if (errCode == UserUnknownError.ERROR_CODE) { - throw new UserUnknownError (); - } else if (errCode == TerminalUnknownError.ERROR_CODE) { - throw new TerminalUnknownError (); - } else { - if (errMessage.ToLower ().Contains ("pairing has been deactivated") - || errMessage.ToLower ().Contains ("pairing has not been authorized")) { - throw new PairingDeactivatedError (); - } else { - throw new RequestError (errMessage); + try { + return SimpleJson.SimpleJson.DeserializeObject (response); + } catch (Exception ex) { + throw new RequestError ("Could not parse response", ex); + } + } } - } - } - public class AdvancedApiUsageFactory - { - public ToopherApi.AdvancedApiUsageFactory.Pairings pairings; - - public AdvancedApiUsageFactory (ToopherApi toopherApi) - { - this.pairings = new ToopherApi.AdvancedApiUsageFactory.Pairings(toopherApi); - } + public JsonObject get (string endpoint, NameValueCollection parameters = null) + { + return (JsonObject)request ("GET", endpoint, parameters); + } - public class Pairings - { - private ToopherApi api; + public JsonArray getArray (string endpoint, NameValueCollection parameters = null) + { + return (JsonArray)request ("GET", endpoint, parameters); + } - public Pairings (ToopherApi toopherApi) + public JsonObject post (string endpoint, NameValueCollection parameters = null) { - this.api = toopherApi; + return (JsonObject) request ("POST", endpoint, parameters); } - public Pairing GetById (string pairingId) + private void parseRequestError (JsonObject err) { - string endpoint = string.Format ("pairings/{0}", pairingId); - var json = api.get (endpoint); - return new Pairing (json); + long errCode = (long)err["error_code"]; + string errMessage = (string)err["error_message"]; + if (errCode == UserDisabledError.ERROR_CODE) { + throw new UserDisabledError (); + } else if (errCode == UserUnknownError.ERROR_CODE) { + throw new UserUnknownError (); + } else if (errCode == TerminalUnknownError.ERROR_CODE) { + throw new TerminalUnknownError (); + } else { + if (errMessage.ToLower ().Contains ("pairing has been deactivated") + || errMessage.ToLower ().Contains ("pairing has not been authorized")) { + throw new PairingDeactivatedError (); + } else { + throw new RequestError (errMessage); + } + } } } } diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 58ea363..95deb88 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -329,5 +329,12 @@ public void GenerateAdvancedPairings () var api = getApi(); Assert.IsInstanceOf (api.advanced.pairings); } + + [Test] + public void GenerateAdvancedRaw () + { + var api = getApi(); + Assert.IsInstanceOf (api.advanced.raw); + } } } From d7d884e0151a5f6ce0730af0cbc032a422046001 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 11:50:57 -0600 Subject: [PATCH 015/103] Add ToopherApi.AdvancedApiUsageFactory.AuthenticationRequests --- ToopherDotNet/ToopherDotNet.cs | 19 +++++++++++++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 15 +++++++++++---- 2 files changed, 30 insertions(+), 4 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index af8d8d0..d6c5505 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -180,11 +180,13 @@ public string GetPairingResetLink (string pairingId, string securityQuestion = n public class AdvancedApiUsageFactory { public ToopherApi.AdvancedApiUsageFactory.Pairings pairings; + public ToopherApi.AdvancedApiUsageFactory.AuthenticationRequests authenticationRequests; public ToopherApi.AdvancedApiUsageFactory.ApiRawRequester raw; public AdvancedApiUsageFactory (ToopherApi toopherApi) { this.pairings = new ToopherApi.AdvancedApiUsageFactory.Pairings(toopherApi); + this.authenticationRequests = new ToopherApi.AdvancedApiUsageFactory.AuthenticationRequests(toopherApi); this.raw = new ToopherApi.AdvancedApiUsageFactory.ApiRawRequester(toopherApi); } @@ -205,6 +207,23 @@ public Pairing GetById (string pairingId) } } + public class AuthenticationRequests + { + private ToopherApi api; + + public AuthenticationRequests (ToopherApi toopherApi) + { + this.api = toopherApi; + } + + public AuthenticationRequest GetById (string authenticationRequestId) + { + string endpoint = string.Format ("authentication_requests/{0}", authenticationRequestId); + var json = api.advanced.raw.get (endpoint); + return new AuthenticationRequest (json); + } + } + public class ApiRawRequester { private ToopherApi api; diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 95deb88..a3295d5 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -201,11 +201,11 @@ public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest () } [Test] - public void AccessArbitraryKeysInAuthenticationRequestTest () + public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}, ""random_key"":""84""}"; - AuthenticationRequest auth = api.GetAuthenticationRequest ("1"); + AuthenticationRequest auth = api.advanced.authenticationRequests.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (auth.id, "1"); Assert.IsFalse (auth.pending); @@ -267,11 +267,11 @@ public void UnauthorizedPairingRaisesCorrectErrorTest () } [Test] - public void GetAuthenticationRequestTest () + public void AdvancedAuthenticationRequestsGetByIdTest () { var api = getApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; - AuthenticationRequest auth = api.GetAuthenticationRequest ("1"); + AuthenticationRequest auth = api.advanced.authenticationRequests.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (auth.id, "1"); Assert.IsFalse (auth.pending); @@ -336,5 +336,12 @@ public void GenerateAdvancedRaw () var api = getApi(); Assert.IsInstanceOf (api.advanced.raw); } + + [Test] + public void GenerateAdvancedAuthenticationRequests () + { + var api = getApi(); + Assert.IsInstanceOf (api.advanced.authenticationRequests); + } } } From 6a80a56df7108be12071a7b70fad1dd8f17349c9 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 12:15:13 -0600 Subject: [PATCH 016/103] Add User object and ToopherApi.AdvancedApiUsageFactory.Users.GetById --- ToopherDotNet/ToopherDotNet.cs | 64 ++++++++++++++++++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 33 ++++++++++++ 2 files changed, 97 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index d6c5505..1876361 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -181,12 +181,14 @@ public class AdvancedApiUsageFactory { public ToopherApi.AdvancedApiUsageFactory.Pairings pairings; public ToopherApi.AdvancedApiUsageFactory.AuthenticationRequests authenticationRequests; + public ToopherApi.AdvancedApiUsageFactory.Users users; public ToopherApi.AdvancedApiUsageFactory.ApiRawRequester raw; public AdvancedApiUsageFactory (ToopherApi toopherApi) { this.pairings = new ToopherApi.AdvancedApiUsageFactory.Pairings(toopherApi); this.authenticationRequests = new ToopherApi.AdvancedApiUsageFactory.AuthenticationRequests(toopherApi); + this.users = new ToopherApi.AdvancedApiUsageFactory.Users(toopherApi); this.raw = new ToopherApi.AdvancedApiUsageFactory.ApiRawRequester(toopherApi); } @@ -224,6 +226,23 @@ public AuthenticationRequest GetById (string authenticationRequestId) } } + public class Users + { + private ToopherApi api; + + public Users (ToopherApi toopherApi) + { + this.api = toopherApi; + } + + public User GetById (string userId) + { + string endpoint = string.Format ("users/{0}", userId); + var json = api.advanced.raw.get (endpoint); + return new User (json); + } + } + public class ApiRawRequester { private ToopherApi api; @@ -484,6 +503,51 @@ public AuthenticationRequest (IDictionary _dict) } } + public class User + { + private IDictionary _dict; + public object this[string key] + { + get + { + return _dict[key]; + } + } + + public string id + { + get; + private set; + } + public string name + { + get; + private set; + } + public bool toopherAuthenticationEnabled + { + get; + private set; + } + + public override string ToString () + { + return string.Format ("[User: id={0}; name={1}; toopherAuthenticationEnabled={2}]", id, name, toopherAuthenticationEnabled); + } + + public User (IDictionary _dict) + { + this._dict = _dict; + try { + this.id = (string)_dict["id"]; + this.name = (string)_dict["name"]; + this.toopherAuthenticationEnabled = (bool)_dict["toopher_authentication_enabled"]; + } catch (Exception ex) { + throw new RequestError ("Could not parse user from response", ex); + } + } + } + // An exception class used to indicate an error in a request public class RequestError : System.ApplicationException { diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index a3295d5..4d8b6cd 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -216,6 +216,19 @@ public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest () Assert.AreEqual (auth["random_key"], "84"); } + [Test] + public void AccessArbitraryKeysInAdvancedUsersGetByIdTest () + { + var api = getApi (); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true, ""random_key"":""84""}"; + User user = api.advanced.users.GetById ("1"); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual (user.id, "1"); + Assert.AreEqual (user.name, "username"); + Assert.IsTrue (user.toopherAuthenticationEnabled); + Assert.AreEqual (user["random_key"], "84"); + } + [Test] [ExpectedException(typeof(UserDisabledError))] public void DisabledUserRaisesCorrectErrorTest () @@ -307,6 +320,19 @@ public void AdvancedPairingsGetByIdTest () Assert.IsTrue (pairing.enabled); } + [Test] + public void AdvancedUsersGetByIdTest () + { + var api = getApi (); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}"; + User user = api.advanced.users.GetById ("1"); + Assert.IsInstanceOf (user); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual (user.id, "1"); + Assert.AreEqual (user.name, "username"); + Assert.IsTrue (user.toopherAuthenticationEnabled); + } + [Test] public void GeneratePairingLinkTest () { @@ -343,5 +369,12 @@ public void GenerateAdvancedAuthenticationRequests () var api = getApi(); Assert.IsInstanceOf (api.advanced.authenticationRequests); } + + [Test] + public void GenerateAdvancedUsers () + { + var api = getApi(); + Assert.IsInstanceOf (api.advanced.users); + } } } From 85ecffb5ab986f090ffe13c4b1e81fc81b5e0b74 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 12:26:19 -0600 Subject: [PATCH 017/103] Add ToopherApi.AdvancedApiUsageFactory.Users.Create --- ToopherDotNet/ToopherDotNet.cs | 11 +++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 29 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 1876361..d265d04 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -241,6 +241,17 @@ public User GetById (string userId) var json = api.advanced.raw.get (endpoint); return new User (json); } + + public User Create (string userName, NameValueCollection parameters = null) + { + string endpoint = "users/create"; + if (parameters == null) { + parameters = new NameValueCollection (); + } + parameters.Add ("name", userName); + var json = api.advanced.raw.post (endpoint, parameters); + return new User (json); + } } public class ApiRawRequester diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 4d8b6cd..78805d5 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -333,6 +333,35 @@ public void AdvancedUsersGetByIdTest () Assert.IsTrue (user.toopherAuthenticationEnabled); } + [Test] + public void AdvancedUsersCreateTest () + { + var api = getApi (); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}"; + User user = api.advanced.users.Create ("username"); + Assert.IsInstanceOf (user); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (user.id, "1"); + Assert.AreEqual (user.name, "username"); + Assert.IsTrue (user.toopherAuthenticationEnabled); + } + + [Test] + public void AdvancedUsersCreateWithParamsTest () + { + var api = getApi(); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}"; + NameValueCollection parameters = new NameValueCollection(); + parameters.Add ("foo", "bar"); + User user = api.advanced.users.Create ("username", parameters); + Assert.IsInstanceOf (user); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (WebClientMock.LastRequestData["foo"], "bar"); + Assert.AreEqual (user.id, "1"); + Assert.AreEqual (user.name, "username"); + Assert.IsTrue (user.toopherAuthenticationEnabled); + } + [Test] public void GeneratePairingLinkTest () { From 41fa5ee53206d30db8d55adb7daa370e11fa217e Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 13:18:52 -0600 Subject: [PATCH 018/103] Add UserTerminal object and ToopherApi.AdvancedApiUsageFactory.UserTerminals.GetById --- ToopherDotNet/ToopherDotNet.cs | 66 ++++++++++++++++++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 21 ++++++++ 2 files changed, 87 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index d265d04..22ea4b7 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -182,6 +182,7 @@ public class AdvancedApiUsageFactory public ToopherApi.AdvancedApiUsageFactory.Pairings pairings; public ToopherApi.AdvancedApiUsageFactory.AuthenticationRequests authenticationRequests; public ToopherApi.AdvancedApiUsageFactory.Users users; + public ToopherApi.AdvancedApiUsageFactory.UserTerminals userTerminals; public ToopherApi.AdvancedApiUsageFactory.ApiRawRequester raw; public AdvancedApiUsageFactory (ToopherApi toopherApi) @@ -189,6 +190,7 @@ public AdvancedApiUsageFactory (ToopherApi toopherApi) this.pairings = new ToopherApi.AdvancedApiUsageFactory.Pairings(toopherApi); this.authenticationRequests = new ToopherApi.AdvancedApiUsageFactory.AuthenticationRequests(toopherApi); this.users = new ToopherApi.AdvancedApiUsageFactory.Users(toopherApi); + this.userTerminals = new ToopherApi.AdvancedApiUsageFactory.UserTerminals(toopherApi); this.raw = new ToopherApi.AdvancedApiUsageFactory.ApiRawRequester(toopherApi); } @@ -254,6 +256,23 @@ public User Create (string userName, NameValueCollection parameters = null) } } + public class UserTerminals + { + private ToopherApi api; + + public UserTerminals (ToopherApi toopherApi) + { + this.api = toopherApi; + } + + public UserTerminal GetById (string userTerminalId) + { + string endpoint = string.Format ("user_terminals/{0}", userTerminalId); + var json = api.advanced.raw.get (endpoint); + return new UserTerminal (json); + } + } + public class ApiRawRequester { private ToopherApi api; @@ -559,6 +578,53 @@ public User (IDictionary _dict) } } + public class UserTerminal + { + private IDictionary _dict; + public object this[string key] + { + get + { + return _dict[key]; + } + } + + public string id + { + get; + private set; + } + public string name + { + get; + private set; + } + public string requesterSpecifiedId + { + get; + private set; + } + public User user; + + public override string ToString () + { + return string.Format ("[UserTerminal: id={0}; name={1}; requesterSpecifiedId={2}; userId={3}, userName={4}, userToopherAuthenticationEnabled={5}]", id, name, requesterSpecifiedId, user.id, user.name, user.toopherAuthenticationEnabled); + } + + public UserTerminal (IDictionary _dict) + { + try { + this._dict = _dict; + this.id = (string)_dict["id"]; + this.name = (string)_dict["name"]; + this.requesterSpecifiedId = (string)_dict["requester_specified_id"]; + this.user = new User ((JsonObject)_dict["user"]); + } catch (Exception ex) { + throw new RequestError ("Could not parse pairing status from response", ex); + } + } + } + // An exception class used to indicate an error in a request public class RequestError : System.ApplicationException { diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 78805d5..5cb0eb6 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -362,6 +362,20 @@ public void AdvancedUsersCreateWithParamsTest () Assert.IsTrue (user.toopherAuthenticationEnabled); } + [Test] + public void AdvancedUserTerminalsGetByIdTest () + { + var api = getApi (); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; + UserTerminal userTerminal = api.advanced.userTerminals.GetById ("1"); + Assert.IsInstanceOf (userTerminal); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual (userTerminal.id, "1"); + Assert.AreEqual (userTerminal.name, "userTerminalName"); + Assert.AreEqual (userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); + Assert.IsInstanceOf (userTerminal.user); + } + [Test] public void GeneratePairingLinkTest () { @@ -405,5 +419,12 @@ public void GenerateAdvancedUsers () var api = getApi(); Assert.IsInstanceOf (api.advanced.users); } + + [Test] + public void GenerateAdvancedUserTerminals () + { + var api = getApi(); + Assert.IsInstanceOf (api.advanced.userTerminals); + } } } From ac28a902065022d6f56cb620918554ed321c4059 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 13:38:19 -0600 Subject: [PATCH 019/103] Add ToopherApi.AdvancedApiUsageFactory.UserTerminals.Create --- ToopherDotNet/ToopherDotNet.cs | 13 +++++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 29 ++++++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 22ea4b7..5a2c054 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -271,6 +271,19 @@ public UserTerminal GetById (string userTerminalId) var json = api.advanced.raw.get (endpoint); return new UserTerminal (json); } + + public UserTerminal Create (string userName, string terminalName, string requesterSpecifiedId, NameValueCollection parameters = null) + { + string endpoint = "user_terminals/create"; + if (parameters == null) { + parameters = new NameValueCollection (); + } + parameters.Add ("user_name", userName); + parameters.Add ("terminal_name", terminalName); + parameters.Add ("requester_specified_id", requesterSpecifiedId); + var json = api.advanced.raw.post (endpoint, parameters); + return new UserTerminal (json); + } } public class ApiRawRequester diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 5cb0eb6..a448864 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -376,6 +376,35 @@ public void AdvancedUserTerminalsGetByIdTest () Assert.IsInstanceOf (userTerminal.user); } + [Test] + public void AdvancedUserTerminalsCreateTest () + { + var api = getApi (); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; + UserTerminal userTerminal = api.advanced.userTerminals.Create ("userName", "userTerminalName", "requesterSpecifiedId"); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (userTerminal.id, "1"); + Assert.AreEqual (userTerminal.name, "userTerminalName"); + Assert.AreEqual (userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); + Assert.IsInstanceOf (userTerminal.user); + } + + [Test] + public void AdvancedUserTerminalsCreateWithParamsTest () + { + var api = getApi (); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; + NameValueCollection parameters = new NameValueCollection(); + parameters.Add ("foo", "bar"); + UserTerminal userTerminal = api.advanced.userTerminals.Create ("userName", "userTerminalName", "requesterSpecifiedId", parameters); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (WebClientMock.LastRequestData["foo"], "bar"); + Assert.AreEqual (userTerminal.id, "1"); + Assert.AreEqual (userTerminal.name, "userTerminalName"); + Assert.AreEqual (userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); + Assert.IsInstanceOf (userTerminal.user); + } + [Test] public void GeneratePairingLinkTest () { From a94ffe19b2c4c92f2068aaebbf079962af553c16 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 13:54:40 -0600 Subject: [PATCH 020/103] Add ToopherApi.AdvancedApiUsageFactory.Users.GetByName --- ToopherDotNet/ToopherDotNet.cs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 5a2c054..02dc72e 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -244,6 +244,25 @@ public User GetById (string userId) return new User (json); } + public User GetByName (string userName) + { + string endpoint = "users"; + NameValueCollection parameters = new NameValueCollection (); + parameters.Add ("name", userName); + JsonArray json = api.advanced.raw.getArray (endpoint, parameters); + + if (json.Count() > 1) { + throw new RequestError (string.Format ("More than one user with name {0}", userName)); + } + if (json.Count() == 0) { + throw new RequestError (string.Format ("No users with name {0}", userName)); + } + Console.WriteLine (json); + var user = (JsonObject)json[0]; + string userId = user["id"].ToString (); + return GetById (userId); + } + public User Create (string userName, NameValueCollection parameters = null) { string endpoint = "users/create"; From 08d580a934c20e22b2c64c1baf8ea53cfaa80bbe Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 14:04:03 -0600 Subject: [PATCH 021/103] Update Pairing field names --- ToopherDotNet/ToopherDotNet.cs | 34 ++++++++---------------- ToopherDotNetTests/ToopherDotNetTests.cs | 20 +++++++------- 2 files changed, 21 insertions(+), 33 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 02dc72e..de22362 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -433,13 +433,13 @@ private void parseRequestError (JsonObject err) // Status information for a pairing request public class Pairing { - private IDictionary _dict; + private IDictionary rawResponse; public object this[string key] { get { - return _dict[key]; + return rawResponse[key]; } } @@ -448,16 +448,6 @@ public string id get; private set; } - public string userId - { - get; - private set; - } - public string userName - { - get; - private set; - } public bool pending { get; @@ -468,28 +458,26 @@ public bool enabled get; private set; } + public User user; public override string ToString () { - return string.Format ("[Pairing: id={0}; userId={1}; userName={2}, enabled={3}, pending={4}]", id, userId, userName, enabled, pending); + return string.Format ("[Pairing: id={0}; enabled={1}; pending={2}; userId={3}; userName={4}; userToopherAuthenticationEnabled={5}]", id, enabled, pending, user.id, user.name, user.toopherAuthenticationEnabled); } - public Pairing (IDictionary _dict) + public Pairing (IDictionary response) { + this.rawResponse = response; try { - this._dict = _dict; - this.id = (string)_dict["id"]; - this.pending = (bool)_dict["pending"]; - this.enabled = (bool)_dict["enabled"]; - - var user = (JsonObject)_dict["user"]; - this.userId = (string)user["id"]; - this.userName = (string)user["name"]; + this.rawResponse = response; + this.id = (string)response["id"]; + this.pending = (bool)response["pending"]; + this.enabled = (bool)response["enabled"]; + this.user = new User ((JsonObject)response["user"]); } catch (Exception ex) { throw new RequestError ("Could not parse pairing status from response", ex); } } - } // Status information for an authentication request diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index a448864..fa54e17 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -107,7 +107,7 @@ public void ToopherBaseUrlTest () public void CreatePairingWithPhraseTest () { var api = getApi(); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; Pairing pairing = api.Pair ("some user", "awkward turtle"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["pairing_phrase"], "awkward turtle"); @@ -119,7 +119,7 @@ public void CreatePairingWithPhraseTest () public void CreateSmsPairingTest () { var api = getApi(); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; Pairing pairing = api.Pair ("some user", "555-555-5555"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["phone_number"], "555-555-5555"); @@ -131,7 +131,7 @@ public void CreateSmsPairingTest () public void CreateQrPairingTest () { var api = getApi(); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; Pairing pairing = api.Pair ("some user"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some user"); @@ -168,7 +168,7 @@ public void AuthenticateWithUsernameAndExtrasTest () public void ArbitraryParametersOnPairTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; Pairing pairing = api.Pair ("awkward turtle", "some user", extras: new Dictionary(){{"test_param", "42"}}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); @@ -190,12 +190,12 @@ public void ArbitraryParamtersOnAuthenticateTest () public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}, ""random_key"":""84""}"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""random_key"":""84""}"; Pairing pairing = api.advanced.pairings.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (pairing.id, "1"); - Assert.AreEqual (pairing.userName, "some user"); - Assert.AreEqual (pairing.userId, "1"); + Assert.AreEqual (pairing.user.name, "some user"); + Assert.AreEqual (pairing.user.id, "1"); Assert.IsTrue (pairing.enabled); Assert.AreEqual (pairing["random_key"], "84"); } @@ -310,13 +310,13 @@ public void GetAuthenticationRequestWIthOtpTest () public void AdvancedPairingsGetByIdTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user""}}"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; Pairing pairing = api.advanced.pairings.GetById ("1"); Assert.IsInstanceOf (pairing); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (pairing.id, "1"); - Assert.AreEqual (pairing.userName, "some user"); - Assert.AreEqual (pairing.userId, "1"); + Assert.AreEqual (pairing.user.name, "some user"); + Assert.AreEqual (pairing.user.id, "1"); Assert.IsTrue (pairing.enabled); } From f6f0ce9086f42a1cdc28999d67430a7358ab9b55 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 15:01:26 -0600 Subject: [PATCH 022/103] Add Pairing.api --- ToopherDotNet/ToopherDotNet.cs | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index de22362..c54ad67 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -83,7 +83,7 @@ public Pairing Pair (string userName, string pairingPhraseOrNum = null, Dictiona } var json = advanced.raw.post (endpoint, parameters); - return new Pairing (json); + return new Pairing (json, this); } // Initiate an authentication request by pairing id or username @@ -207,7 +207,7 @@ public Pairing GetById (string pairingId) { string endpoint = string.Format ("pairings/{0}", pairingId); var json = api.advanced.raw.get (endpoint); - return new Pairing (json); + return new Pairing (json, api); } } @@ -459,23 +459,27 @@ public bool enabled private set; } public User user; + public ToopherApi api; public override string ToString () { return string.Format ("[Pairing: id={0}; enabled={1}; pending={2}; userId={3}; userName={4}; userToopherAuthenticationEnabled={5}]", id, enabled, pending, user.id, user.name, user.toopherAuthenticationEnabled); } - public Pairing (IDictionary response) + public Pairing (IDictionary response, ToopherApi toopherApi) { this.rawResponse = response; try { - this.rawResponse = response; this.id = (string)response["id"]; this.pending = (bool)response["pending"]; this.enabled = (bool)response["enabled"]; this.user = new User ((JsonObject)response["user"]); + this.api = toopherApi; } catch (Exception ex) { - throw new RequestError ("Could not parse pairing status from response", ex); + throw new RequestError ("Could not parse pairing from response", ex); + } + } + } } } From c34d7ca42bce40083cfb4dc1212e077d70110b15 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 15:03:18 -0600 Subject: [PATCH 023/103] Add Pairing.RefreshFromServer and Update --- ToopherDotNet/ToopherDotNet.cs | 19 +++++++++++++++++-- ToopherDotNetTests/ToopherDotNetTests.cs | 16 ++++++++++++++++ 2 files changed, 33 insertions(+), 2 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index c54ad67..d7686be 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -434,6 +434,7 @@ private void parseRequestError (JsonObject err) public class Pairing { private IDictionary rawResponse; + private ToopherApi api; public object this[string key] { @@ -459,7 +460,6 @@ public bool enabled private set; } public User user; - public ToopherApi api; public override string ToString () { @@ -469,17 +469,32 @@ public override string ToString () public Pairing (IDictionary response, ToopherApi toopherApi) { this.rawResponse = response; + this.api = toopherApi; try { this.id = (string)response["id"]; this.pending = (bool)response["pending"]; this.enabled = (bool)response["enabled"]; this.user = new User ((JsonObject)response["user"]); - this.api = toopherApi; } catch (Exception ex) { throw new RequestError ("Could not parse pairing from response", ex); } } + public void RefreshFromServer () + { + string endpoint = string.Format ("pairings/{0}", id); + var json = api.advanced.raw.get (endpoint); + Update (json); + } + + private void Update (IDictionary response) + { + this.rawResponse = response; + try { + this.pending = (bool)response["pending"]; + this.enabled = (bool)response["enabled"]; + } catch (Exception ex) { + throw new RequestError ("Could not parse pairing from response", ex); } } } diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index fa54e17..bfa0548 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -5,6 +5,7 @@ using System.Collections.Specialized; using Toopher; using NUnit.Framework; +using SimpleJson; namespace ToopherDotNetTests { @@ -455,5 +456,20 @@ public void GenerateAdvancedUserTerminals () var api = getApi(); Assert.IsInstanceOf (api.advanced.userTerminals); } + + [Test] + public void PairingRefreshFromServerTest () + { + var api = getApi(); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""enabled"":false, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; + Pairing pairing = new Pairing (response, api); + pairing.RefreshFromServer (); + Assert.IsInstanceOf (pairing); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual (pairing.id, "1"); + Assert.IsFalse (pairing.enabled); + Assert.IsTrue (pairing.pending); + } } } From fe57805921f528add99da1d84e3a6882b4c806e3 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 15:17:30 -0600 Subject: [PATCH 024/103] Add UserTerminal.api and update field names --- ToopherDotNet/ToopherDotNet.cs | 25 ++++++++++++++----------- 1 file changed, 14 insertions(+), 11 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index d7686be..b3b2a08 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -288,7 +288,7 @@ public UserTerminal GetById (string userTerminalId) { string endpoint = string.Format ("user_terminals/{0}", userTerminalId); var json = api.advanced.raw.get (endpoint); - return new UserTerminal (json); + return new UserTerminal (json, api); } public UserTerminal Create (string userName, string terminalName, string requesterSpecifiedId, NameValueCollection parameters = null) @@ -301,7 +301,7 @@ public UserTerminal Create (string userName, string terminalName, string request parameters.Add ("terminal_name", terminalName); parameters.Add ("requester_specified_id", requesterSpecifiedId); var json = api.advanced.raw.post (endpoint, parameters); - return new UserTerminal (json); + return new UserTerminal (json, api); } } @@ -619,12 +619,14 @@ public User (IDictionary _dict) public class UserTerminal { - private IDictionary _dict; + private IDictionary rawResponse; + private ToopherApi api; + public object this[string key] { get { - return _dict[key]; + return rawResponse[key]; } } @@ -650,16 +652,17 @@ public override string ToString () return string.Format ("[UserTerminal: id={0}; name={1}; requesterSpecifiedId={2}; userId={3}, userName={4}, userToopherAuthenticationEnabled={5}]", id, name, requesterSpecifiedId, user.id, user.name, user.toopherAuthenticationEnabled); } - public UserTerminal (IDictionary _dict) + public UserTerminal (IDictionary response, ToopherApi toopherApi) { + this.rawResponse = response; + this.api = toopherApi; try { - this._dict = _dict; - this.id = (string)_dict["id"]; - this.name = (string)_dict["name"]; - this.requesterSpecifiedId = (string)_dict["requester_specified_id"]; - this.user = new User ((JsonObject)_dict["user"]); + this.id = (string)response["id"]; + this.name = (string)response["name"]; + this.requesterSpecifiedId = (string)response["requester_specified_id"]; + this.user = new User ((JsonObject)response["user"], toopherApi); } catch (Exception ex) { - throw new RequestError ("Could not parse pairing status from response", ex); + throw new RequestError ("Could not parse user terminal from response", ex); } } } From 1f7b17b36098ded61a5fd498e3dfa10e019e3c63 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 15:18:38 -0600 Subject: [PATCH 025/103] Add User.api and update field names --- ToopherDotNet/ToopherDotNet.cs | 23 +++++++++++++---------- 1 file changed, 13 insertions(+), 10 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index b3b2a08..a9bc010 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -241,7 +241,7 @@ public User GetById (string userId) { string endpoint = string.Format ("users/{0}", userId); var json = api.advanced.raw.get (endpoint); - return new User (json); + return new User (json, api); } public User GetByName (string userName) @@ -271,7 +271,7 @@ public User Create (string userName, NameValueCollection parameters = null) } parameters.Add ("name", userName); var json = api.advanced.raw.post (endpoint, parameters); - return new User (json); + return new User (json, api); } } @@ -474,7 +474,7 @@ public Pairing (IDictionary response, ToopherApi toopherApi) this.id = (string)response["id"]; this.pending = (bool)response["pending"]; this.enabled = (bool)response["enabled"]; - this.user = new User ((JsonObject)response["user"]); + this.user = new User ((JsonObject)response["user"], toopherApi); } catch (Exception ex) { throw new RequestError ("Could not parse pairing from response", ex); } @@ -574,12 +574,14 @@ public AuthenticationRequest (IDictionary _dict) public class User { - private IDictionary _dict; + private IDictionary rawResponse; + private ToopherApi api; + public object this[string key] { get { - return _dict[key]; + return rawResponse[key]; } } @@ -604,13 +606,14 @@ public override string ToString () return string.Format ("[User: id={0}; name={1}; toopherAuthenticationEnabled={2}]", id, name, toopherAuthenticationEnabled); } - public User (IDictionary _dict) + public User (IDictionary response, ToopherApi toopherApi) { - this._dict = _dict; + this.rawResponse = response; + this.api = toopherApi; try { - this.id = (string)_dict["id"]; - this.name = (string)_dict["name"]; - this.toopherAuthenticationEnabled = (bool)_dict["toopher_authentication_enabled"]; + this.id = (string)response["id"]; + this.name = (string)response["name"]; + this.toopherAuthenticationEnabled = (bool)response["toopher_authentication_enabled"]; } catch (Exception ex) { throw new RequestError ("Could not parse user from response", ex); } From c2be0f1075c30492dc143fb176a2a2251963a0f4 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 15:32:26 -0600 Subject: [PATCH 026/103] Add User.RefreshFromServer and Update --- ToopherDotNet/ToopherDotNet.cs | 21 +++++++++++++++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 20 ++++++++++++++++++-- 2 files changed, 39 insertions(+), 2 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index a9bc010..b1640e1 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -493,6 +493,7 @@ private void Update (IDictionary response) try { this.pending = (bool)response["pending"]; this.enabled = (bool)response["enabled"]; + this.user.Update ((JsonObject)response["user"]); } catch (Exception ex) { throw new RequestError ("Could not parse pairing from response", ex); } @@ -618,6 +619,26 @@ public User (IDictionary response, ToopherApi toopherApi) throw new RequestError ("Could not parse user from response", ex); } } + + public void RefreshFromServer () + { + string endpoint = string.Format ("users/{0}", id); + var json = api.advanced.raw.get(endpoint); + Update (json); + } + + public void Update (IDictionary response) + { + this.rawResponse = response; + try { + this.name = (string)response["name"]; + this.toopherAuthenticationEnabled = (bool)response["toopher_authentication_enabled"]; + } catch (Exception ex) { + throw new RequestError ("Could not parse user from response", ex); + } + } + + } public class UserTerminal diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index bfa0548..72a088e 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -461,15 +461,31 @@ public void GenerateAdvancedUserTerminals () public void PairingRefreshFromServerTest () { var api = getApi(); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""enabled"":false, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""enabled"":false, ""user"":{""id"":""1"",""name"":""userNameChanged"", ""toopher_authentication_enabled"":true}}"; Pairing pairing = new Pairing (response, api); pairing.RefreshFromServer (); Assert.IsInstanceOf (pairing); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (pairing.id, "1"); + Assert.AreEqual (pairing.user.name, "userNameChanged"); Assert.IsFalse (pairing.enabled); Assert.IsTrue (pairing.pending); } + + [Test] + public void UserRefreshFromServerTest () + { + var api = getApi(); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":false}"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userNameChanged"", ""toopher_authentication_enabled"":true}"; + User user = new User (response, api); + user.RefreshFromServer (); + Assert.IsInstanceOf (user); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual (user.id, "1"); + Assert.AreEqual (user.name, "userNameChanged"); + Assert.IsTrue (user.toopherAuthenticationEnabled); + } } } From 6f636fabfe52bea4b46cc16bd321e7f687bf4c99 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 15:44:52 -0600 Subject: [PATCH 027/103] Add UserTerminal.RefreshFromServer and Update --- ToopherDotNet/ToopherDotNet.cs | 19 +++++++++++++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 16 ++++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index b1640e1..5cd3a36 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -689,6 +689,25 @@ public UserTerminal (IDictionary response, ToopherApi toopherApi throw new RequestError ("Could not parse user terminal from response", ex); } } + + public void RefreshFromServer () + { + string endpoint = string.Format ("user_terminals/{0}", id); + var json = api.advanced.raw.get (endpoint); + Update (json); + } + + public void Update (IDictionary response) + { + this.rawResponse = response; + try { + this.name = (string)response["name"]; + this.requesterSpecifiedId = (string)response["requester_specified_id"]; + this.user.Update ((JsonObject)response["user"]); + } catch (Exception ex) { + throw new RequestError ("Could not parse user terminal from response", ex); + } + } } // An exception class used to indicate an error in a request diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 72a088e..c4864ad 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -487,5 +487,21 @@ public void UserRefreshFromServerTest () Assert.AreEqual (user.name, "userNameChanged"); Assert.IsTrue (user.toopherAuthenticationEnabled); } + + [Test] + public void UserTerminalRefreshFromServerTest () + { + var api = getApi(); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalNameChanged"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userNameChanged"", ""toopher_authentication_enabled"":false}}"; + UserTerminal userTerminal = new UserTerminal (response, api); + userTerminal.RefreshFromServer (); + Assert.IsInstanceOf (userTerminal); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual (userTerminal.id, "1"); + Assert.AreEqual (userTerminal.name, "userTerminalNameChanged"); + Assert.AreEqual (userTerminal.user.name, "userNameChanged"); + Assert.IsFalse (userTerminal.user.toopherAuthenticationEnabled); + } } } From b4e250424aa604930367e73a9f10bb9899d1ec0d Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 15:53:33 -0600 Subject: [PATCH 028/103] Add Action object --- ToopherDotNet/ToopherDotNet.cs | 39 ++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 5cd3a36..ed25a70 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -710,6 +710,45 @@ public void Update (IDictionary response) } } + public class Action + { + private IDictionary rawResponse; + public object this[string key] + { + get + { + return rawResponse[key]; + } + } + + public string id + { + get; + private set; + } + public string name + { + get; + private set; + } + + public override string ToString () + { + return string.Format ("[Action: id={0}; name={1}]", id, name); + } + + public Action (IDictionary response) + { + this.rawResponse = response; + try { + this.id = (string)response["id"]; + this.name = (string)response["name"]; + } catch (Exception ex) { + throw new RequestError ("Could not parse action from response", ex); + } + } + } + // An exception class used to indicate an error in a request public class RequestError : System.ApplicationException { From 207270b8a659f3b7e223e5ffde8be7099a55a16b Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Feb 2015 18:11:00 -0600 Subject: [PATCH 029/103] Add AuthenticationRequest.api and update field names --- ToopherDotNet/ToopherDotNet.cs | 47 ++++++++++++------------ ToopherDotNetTests/ToopherDotNetTests.cs | 18 ++++----- 2 files changed, 33 insertions(+), 32 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index ed25a70..270fb86 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -116,7 +116,7 @@ public AuthenticationRequest Authenticate (string pairingIdOrUsername, string te } var json = advanced.raw.post (endpoint, parameters); - return new AuthenticationRequest (json); + return new AuthenticationRequest (json, this); } /// @@ -224,7 +224,7 @@ public AuthenticationRequest GetById (string authenticationRequestId) { string endpoint = string.Format ("authentication_requests/{0}", authenticationRequestId); var json = api.advanced.raw.get (endpoint); - return new AuthenticationRequest (json); + return new AuthenticationRequest (json, api); } } @@ -503,12 +503,14 @@ private void Update (IDictionary response) // Status information for an authentication request public class AuthenticationRequest { - private IDictionary _dict; + private IDictionary rawResponse; + private ToopherApi api; + public object this[string key] { get { - return _dict[key]; + return rawResponse[key]; } } @@ -532,41 +534,40 @@ public bool automated get; private set; } - public string reason + public int reasonCode { get; private set; } - public string terminalId - { - get; - private set; - } - public string terminalName + public string reason { get; private set; } + public Action action; + public UserTerminal terminal; + public User user; public override string ToString () { - return string.Format ("[AuthenticationRequest: id={0}; pending={1}; granted={2}; automated={3}; reason={4}; terminalId={5}; terminalName={6}]", id, pending, granted, automated, reason, terminalId, terminalName); + return string.Format ("[AuthenticationRequest: id={0}; pending={1}; granted={2}; automated={3}; reasonCode={4}; reason={5}; actionId={6}; actionName={7}; terminalId={8}; terminalName={9}; terminalRequesterSpecifiedId={10}; userId={11}; userName={12}; userToopherAuthenticationEnabled={13}]", id, pending, granted, automated, reasonCode, reason, action.id, action.name, terminal.id, terminal.name, terminal.requesterSpecifiedId, user.id, user.name, user.toopherAuthenticationEnabled); } - public AuthenticationRequest (IDictionary _dict) + public AuthenticationRequest (IDictionary response, ToopherApi toopherApi) { - this._dict = _dict; + this.rawResponse = response; + this.api = toopherApi; try { // validate that the json has the minimum keys we need - this.id = (string)_dict["id"]; - this.pending = (bool)_dict["pending"]; - this.granted = (bool)_dict["granted"]; - this.automated = (bool)_dict["automated"]; - this.reason = (string)_dict["reason"]; - - var terminal = (JsonObject)_dict["terminal"]; - this.terminalId = (string)terminal["id"]; - terminalName = (string)terminal["name"]; + this.id = (string)response["id"]; + this.pending = (bool)response["pending"]; + this.granted = (bool)response["granted"]; + this.automated = (bool)response["automated"]; + this.reasonCode = Convert.ToInt32(response["reason_code"]); + this.reason = (string)response["reason"]; + this.action = new Action((JsonObject)response["action"]); + this.terminal = new UserTerminal((JsonObject)response["terminal"], toopherApi); + this.user = new User((JsonObject)response["user"], toopherApi); } catch (Exception ex) { throw new RequestError ("Could not parse authentication status from response", ex); } diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index c4864ad..7bbc50a 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -144,7 +144,7 @@ public void AuthenticateWithPairingIdTest () { var api = getApi (); string pairingId = Guid.NewGuid().ToString(); - WebClientMock.ReturnValue = @"{""id"":""" + pairingId + @""", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; + WebClientMock.ReturnValue = @"{""id"":""" + pairingId + @""", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"",""requester_specified_id"":""requesterSpecifiedId"",""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; AuthenticationRequest auth = api.Authenticate(pairingId, "test terminal"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["pairing_id"], pairingId); @@ -156,7 +156,7 @@ public void AuthenticateWithPairingIdTest () public void AuthenticateWithUsernameAndExtrasTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; AuthenticationRequest auth = api.Authenticate ("some other user", "requester specified id", extras: new Dictionary() {{ "random_key" , "42" }}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some other user"); @@ -180,7 +180,7 @@ public void ArbitraryParametersOnPairTest () public void ArbitraryParamtersOnAuthenticateTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1,""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; AuthenticationRequest auth = api.Authenticate ("1", "test terminal", extras: new Dictionary () { { "test_param", "42" } }); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); @@ -205,15 +205,15 @@ public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest () public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}, ""random_key"":""84""}"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}, ""random_key"":""84""}"; AuthenticationRequest auth = api.advanced.authenticationRequests.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (auth.id, "1"); Assert.IsFalse (auth.pending); Assert.IsTrue (auth.granted); Assert.AreEqual (auth.reason, "its a test"); - Assert.AreEqual (auth.terminalId, "1"); - Assert.AreEqual (auth.terminalName, "test terminal"); + Assert.AreEqual (auth.terminal.id, "1"); + Assert.AreEqual (auth.terminal.name, "test terminal"); Assert.AreEqual (auth["random_key"], "84"); } @@ -284,15 +284,15 @@ public void UnauthorizedPairingRaisesCorrectErrorTest () public void AdvancedAuthenticationRequestsGetByIdTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; AuthenticationRequest auth = api.advanced.authenticationRequests.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (auth.id, "1"); Assert.IsFalse (auth.pending); Assert.IsTrue (auth.granted); Assert.AreEqual (auth.reason, "its a test"); - Assert.AreEqual (auth.terminalId, "1"); - Assert.AreEqual (auth.terminalName, "test terminal"); + Assert.AreEqual (auth.terminal.id, "1"); + Assert.AreEqual (auth.terminal.name, "test terminal"); } [Test] From cddc81b56742a8430101d609744b84eeb9cbe37d Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Feb 2015 10:39:31 -0600 Subject: [PATCH 030/103] Add Action.Update --- ToopherDotNet/ToopherDotNet.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 270fb86..25ccbb5 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -748,6 +748,16 @@ public Action (IDictionary response) throw new RequestError ("Could not parse action from response", ex); } } + + public void Update (IDictionary response) + { + this.rawResponse = response; + try { + this.name = (string)response["name"]; + } catch (Exception ex) { + throw new RequestError ("Could not parse action from response", ex); + } + } } // An exception class used to indicate an error in a request From 06eef93ed6456f7bded5aa104bff0f6cb50f7ec1 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Feb 2015 10:40:00 -0600 Subject: [PATCH 031/103] Add AuthenticationRequest.RefreshFromServer and Update --- ToopherDotNet/ToopherDotNet.cs | 26 +++++++++++++++++++++++- ToopherDotNetTests/ToopherDotNetTests.cs | 17 ++++++++++++++++ 2 files changed, 42 insertions(+), 1 deletion(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 25ccbb5..ebba2ed 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -569,7 +569,31 @@ public AuthenticationRequest (IDictionary response, ToopherApi t this.terminal = new UserTerminal((JsonObject)response["terminal"], toopherApi); this.user = new User((JsonObject)response["user"], toopherApi); } catch (Exception ex) { - throw new RequestError ("Could not parse authentication status from response", ex); + throw new RequestError ("Could not parse authentication request from response", ex); + } + } + + public void RefreshFromServer () + { + string endpoint = string.Format ("authentication_requests/{0}", id); + var json = api.advanced.raw.get(endpoint); + Update (json); + } + + private void Update (IDictionary response) + { + this.rawResponse = response; + try { + this.pending = (bool)response["pending"]; + this.granted = (bool)response["granted"]; + this.automated = (bool)response["automated"]; + this.reasonCode = Convert.ToInt32(response["reason_code"]); + this.reason = (string)response["reason"]; + this.action.Update((JsonObject)response["action"]); + this.terminal.Update((JsonObject)response["terminal"]); + this.user.Update((JsonObject)response["user"]); + } catch (Exception ex) { + throw new RequestError ("Could not parse authentication request from response", ex); } } } diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 7bbc50a..020a612 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -473,6 +473,23 @@ public void PairingRefreshFromServerTest () Assert.IsTrue (pairing.pending); } + [Test] + public void AuthenticationRequestRefreshFromServerTest () + { + var api = getApi(); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""granted"":true, ""automated"":false, ""reason_code"":2, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal CHANGED"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user CHANGED"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName CHANGED""}}"; + AuthenticationRequest auth = new AuthenticationRequest (response, api); + auth.RefreshFromServer (); + Assert.IsInstanceOf (auth); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.IsTrue (auth.pending); + Assert.AreEqual (auth.reasonCode, 2); + Assert.AreEqual (auth.terminal.name, "test terminal CHANGED"); + Assert.AreEqual (auth.user.name, "some user CHANGED"); + Assert.AreEqual (auth.action.name, "actionName CHANGED"); + } + [Test] public void UserRefreshFromServerTest () { From 59454616aeeea070d70536d195167df551f33dc7 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Feb 2015 10:49:49 -0600 Subject: [PATCH 032/103] Add AuthenticationRequest.GrantWithOtp --- ToopherDotNet/ToopherDotNet.cs | 9 +++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 10 ++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index ebba2ed..e5b2305 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -580,6 +580,15 @@ public void RefreshFromServer () Update (json); } + public void GrantWithOtp (string otp) + { + string endpoint = string.Format ("authentication_requests/{0}/otp_auth", id); + NameValueCollection parameters = new NameValueCollection (); + parameters.Add ("otp", otp); + var json = api.advanced.raw.post(endpoint, parameters); + Update (json); + } + private void Update (IDictionary response) { this.rawResponse = response; diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 020a612..ea02f2e 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -296,17 +296,19 @@ public void AdvancedAuthenticationRequestsGetByIdTest () } [Test] - public void GetAuthenticationRequestWIthOtpTest () + public void AuthenticationRequestGrantWithOtpTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal""}}"; - AuthenticationRequest auth = api.GetAuthenticationRequest ("1", otp: "123456"); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":true, ""granted"":false, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; + AuthenticationRequest auth = new AuthenticationRequest (response, api); + auth.GrantWithOtp ("123456"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["otp"], "123456"); Assert.AreEqual (auth.id, "1"); + Assert.IsTrue (auth.granted); } - [Test] public void AdvancedPairingsGetByIdTest () { From 0530085ca95c5fbca3828e0d2c394c8b09a750b4 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Feb 2015 11:03:02 -0600 Subject: [PATCH 033/103] Add User.EnableToopherAuthentication and DisableToopherAuthentication --- ToopherDotNet/ToopherDotNet.cs | 16 ++++++++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 28 ++++++++++++++++++++++++ 2 files changed, 44 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index e5b2305..9ad2def 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -672,7 +672,23 @@ public void Update (IDictionary response) } } + public void EnableToopherAuthentication () + { + string endpoint = string.Format ("users/{0}", id); + NameValueCollection parameters = new NameValueCollection (); + parameters.Add ("toopher_authentication_enabled", "true"); + var json = api.advanced.raw.post (endpoint, parameters); + Update (json); + } + public void DisableToopherAuthentication () + { + string endpoint = string.Format ("users/{0}", id); + NameValueCollection parameters = new NameValueCollection (); + parameters.Add ("toopher_authentication_enabled", "false"); + var json = api.advanced.raw.post (endpoint, parameters); + Update (json); + } } public class UserTerminal diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index ea02f2e..6c6889a 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -507,6 +507,34 @@ public void UserRefreshFromServerTest () Assert.IsTrue (user.toopherAuthenticationEnabled); } + [Test] + public void UserEnableToopherAuthentication () + { + var api = getApi(); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":false}"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}"; + User user = new User (response, api); + user.EnableToopherAuthentication (); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (WebClientMock.LastRequestData["toopher_authentication_enabled"], "true"); + Assert.AreEqual (user.id, "1"); + Assert.IsTrue (user.toopherAuthenticationEnabled); + } + + [Test] + public void UserDisableToopherAuthentication () + { + var api = getApi(); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":false}"; + User user = new User (response, api); + user.DisableToopherAuthentication (); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (WebClientMock.LastRequestData["toopher_authentication_enabled"], "false"); + Assert.AreEqual (user.id, "1"); + Assert.IsFalse (user.toopherAuthenticationEnabled); + } + [Test] public void UserTerminalRefreshFromServerTest () { From a7488b1bab76c37afd34f75b1850cbad4814527b Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Feb 2015 11:07:37 -0600 Subject: [PATCH 034/103] Add User.Reset --- ToopherDotNet/ToopherDotNet.cs | 8 ++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 12 ++++++++++++ 2 files changed, 20 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 9ad2def..a947a61 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -689,6 +689,14 @@ public void DisableToopherAuthentication () var json = api.advanced.raw.post (endpoint, parameters); Update (json); } + + public void Reset () + { + string endpoint = "users/reset"; + NameValueCollection parameters = new NameValueCollection (); + parameters.Add ("name", name); + api.advanced.raw.post(endpoint, parameters); + } } public class UserTerminal diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 6c6889a..7fd9868 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -535,6 +535,18 @@ public void UserDisableToopherAuthentication () Assert.IsFalse (user.toopherAuthenticationEnabled); } + [Test] + public void UserResetTest () + { + var api = getApi(); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":false}"; + User user = new User (response, api); + user.Reset (); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (WebClientMock.LastRequestData["name"], "userName"); + } + [Test] public void UserTerminalRefreshFromServerTest () { From 3c34859ac843a19d7dafd05d8c193f61cba5e42f Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Feb 2015 11:19:20 -0600 Subject: [PATCH 035/103] Add Pairing.GetResetLink --- ToopherDotNet/ToopherDotNet.cs | 13 +++++++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index a947a61..b6e0ee6 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -487,6 +487,19 @@ public void RefreshFromServer () Update (json); } + public string GetResetLink (Dictionary extras = null) + { + string endpoint = string.Format ("pairings/{0}/generate_reset_link", id); + NameValueCollection parameters = new NameValueCollection (); + if (extras != null) { + foreach (KeyValuePair kvp in extras) { + parameters.Add (kvp.Key, kvp.Value); + } + } + var json = api.advanced.raw.post (endpoint, parameters); + return (string)json["url"]; + } + private void Update (IDictionary response) { this.rawResponse = response; diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 7fd9868..9d42686 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -475,6 +475,19 @@ public void PairingRefreshFromServerTest () Assert.IsTrue (pairing.pending); } + [Test] + public void PairingGetResetLinkTest () + { + var api = getApi(); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); + string link = "http://api.toopher.test/v1/pairings/1/reset?reset_authorization=abcde"; + WebClientMock.ReturnValue = @"{""url"":""" + link + @"""}"; + Pairing pairing = new Pairing (response, api); + string returnedLink = pairing.GetResetLink (); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (returnedLink, link); + } + [Test] public void AuthenticationRequestRefreshFromServerTest () { From a057f148344a48cd0e679dc144d234b2a111931b Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Feb 2015 11:23:50 -0600 Subject: [PATCH 036/103] Add Pairing.EmailResetLink --- ToopherDotNet/ToopherDotNet.cs | 13 +++++++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 12 ++++++++++++ 2 files changed, 25 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index b6e0ee6..46f8889 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -500,6 +500,19 @@ public string GetResetLink (Dictionary extras = null) return (string)json["url"]; } + public void EmailResetLink (string email, Dictionary extras = null) + { + string endpoint = string.Format ("pairings/{0}/send_reset_link", id); + NameValueCollection parameters = new NameValueCollection (); + parameters.Add ("reset_email", email); + if (extras != null) { + foreach (KeyValuePair kvp in extras) { + parameters.Add (kvp.Key, kvp.Value); + } + } + api.advanced.raw.post(endpoint, parameters); + } + private void Update (IDictionary response) { this.rawResponse = response; diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 9d42686..0acb228 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -488,6 +488,18 @@ public void PairingGetResetLinkTest () Assert.AreEqual (returnedLink, link); } + [Test] + public void PairingEmailResetLinkTest () + { + var api = getApi(); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); + WebClientMock.ReturnValue = @"{""url"":""[]""}"; + Pairing pairing = new Pairing (response, api); + pairing.EmailResetLink ("test@test.com"); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (WebClientMock.LastRequestData["reset_email"], "test@test.com"); + } + [Test] public void AuthenticationRequestRefreshFromServerTest () { From 0f90aa4f61223b50427495ce8ef6225a5e9001e2 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Feb 2015 13:10:49 -0600 Subject: [PATCH 037/103] Add Pairing.GetQrCodeImage --- ToopherDotNet/ToopherDotNet.cs | 7 +++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 11 +++++++++++ 2 files changed, 18 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 46f8889..44b95f6 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -513,6 +513,13 @@ public void EmailResetLink (string email, Dictionary extras = nu api.advanced.raw.post(endpoint, parameters); } + public byte[] GetQrCodeImage () + { + string endpoint = string.Format ("qr/pairings/{0}", id); + var result = api.advanced.raw.get(endpoint); + return System.Text.Encoding.UTF8.GetBytes (result.ToString()); + } + private void Update (IDictionary response) { this.rawResponse = response; diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 0acb228..66c9480 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -500,6 +500,17 @@ public void PairingEmailResetLinkTest () Assert.AreEqual (WebClientMock.LastRequestData["reset_email"], "test@test.com"); } + [Test] + public void PairingGetQrCodeImage () + { + var api = getApi(); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); + WebClientMock.ReturnValue = @"{}"; + Pairing pairing = new Pairing (response, api); + pairing.GetQrCodeImage (); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + } + [Test] public void AuthenticationRequestRefreshFromServerTest () { From ac6d79f22ce9bb7b83a858e7d1ebd8c7b5c589b1 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Feb 2015 13:39:11 -0600 Subject: [PATCH 038/103] Add test for ToopherApi.AdvancedApiUsageFactory.Users.GetByName --- ToopherDotNet/ToopherDotNet.cs | 1 - ToopherDotNetTests/ToopherDotNetTests.cs | 18 ++++++++++++++++++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 44b95f6..0d17ae1 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -257,7 +257,6 @@ public User GetByName (string userName) if (json.Count() == 0) { throw new RequestError (string.Format ("No users with name {0}", userName)); } - Console.WriteLine (json); var user = (JsonObject)json[0]; string userId = user["id"].ToString (); return GetById (userId); diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 66c9480..0e78ed0 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -49,10 +49,15 @@ class WebClientMock : WebClientProxy static public NameValueCollection LastRequestData { get; set; } static public Exception ReturnException { get; set; } static public string ReturnValue { get; set; } + static public string ReturnValueArray { get; set; } string doit () { if (ReturnException != null) { throw ReturnException; + } else if (ReturnValueArray != null) { + var values = ReturnValueArray; + ReturnValueArray = null; + return values; } else { return ReturnValue; } @@ -336,6 +341,19 @@ public void AdvancedUsersGetByIdTest () Assert.IsTrue (user.toopherAuthenticationEnabled); } + [Test] + public void AdvancedUsersGetByNameTest () + { + var api = getApi (); + WebClientMock.ReturnValueArray = @"[{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}]"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}"; + User user = api.advanced.users.GetByName("username"); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual (user.id, "1"); + Assert.AreEqual (user.name, "username"); + Assert.IsTrue (user.toopherAuthenticationEnabled); + } + [Test] public void AdvancedUsersCreateTest () { From 7287867470b47059193b4355983c25ccf7d710d6 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Feb 2015 13:40:00 -0600 Subject: [PATCH 039/103] Cleanup PairingEmailResetLinkTest --- ToopherDotNetTests/ToopherDotNetTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 0e78ed0..0934dcc 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -511,7 +511,7 @@ public void PairingEmailResetLinkTest () { var api = getApi(); var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); - WebClientMock.ReturnValue = @"{""url"":""[]""}"; + WebClientMock.ReturnValue = @"{}"; Pairing pairing = new Pairing (response, api); pairing.EmailResetLink ("test@test.com"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); From 7d6be3b81c0a31447793ca624df34c01b7debc64 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Mon, 16 Feb 2015 17:43:31 -0600 Subject: [PATCH 040/103] Add ToopherIframe class --- ToopherDotNet/ToopherDotNet.cs | 52 +++++++++++++++++++++++++++++++++- 1 file changed, 51 insertions(+), 1 deletion(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 0d17ae1..edf1e75 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -1,6 +1,7 @@ using System; using OAuth; using System.Net; +using System.Web; using System.Collections.Generic; using System.Collections.Specialized; using System.Text; @@ -9,12 +10,61 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using System.Security.Cryptography; namespace Toopher { - public class ToopherApi + public class ToopherIframe { + private const string IFRAME_VERSION = "2"; + private const long DEFAULT_TTL = 300L; + private const string DEFAULT_BASE_URL = "https://api.toopher.com/v1/"; + + private string baseUrl; + private string consumerKey; + private string consumerSecret; + private Type webClientProxyType; + private static DateTime dateOverride; + + public static void SetDateOverride (DateTime dateOverride) + { + ToopherIframe.dateOverride = dateOverride; + } + + private static DateTime GetDate () + { + if (dateOverride == null) { + return DateTime.UtcNow; + } else { + return dateOverride; + } + } + + private static int GetUnixEpochTimeInSeconds () + { + TimeSpan t = (GetDate() - new DateTime(1970, 1, 1)); + return (int) t.TotalSeconds; + } + public ToopherIframe (string consumerKey, string consumerSecret, string baseUrl = null, Type webClientProxyType = null) + { + this.consumerKey = consumerKey; + this.consumerSecret = consumerSecret; + if (baseUrl != null) { + this.baseUrl = baseUrl; + } else { + this.baseUrl = ToopherApi.DEFAULT_BASE_URL; + } + if (webClientProxyType != null) { + this.webClientProxyType = webClientProxyType; + } else { + this.webClientProxyType = typeof(WebClientProxy); + } + } + { + + public class ToopherApi + { public const string VERSION = "2.0.0"; public const string DEFAULT_BASE_URL = "https://api.toopher.com/v1/"; public ToopherApi.AdvancedApiUsageFactory advanced; From bdc28cda83548e9e33704994f5be3118dd388196 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Mon, 16 Feb 2015 17:46:23 -0600 Subject: [PATCH 041/103] Add ToopherIframe.ValidatePostback and Signature --- ToopherDotNet/ToopherDotNet.cs | 79 ++++++++++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 110 +++++++++++++++++++++++ 2 files changed, 189 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index edf1e75..8acb9e1 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -61,7 +61,86 @@ public ToopherIframe (string consumerKey, string consumerSecret, string baseUrl this.webClientProxyType = typeof(WebClientProxy); } } + + public Dictionary ValidatePostback (Dictionary extras, string sessionToken, long ttl) + { + try { + List missingKeys = new List (); + Dictionary data = new Dictionary(); + + foreach (var entry in extras) + { + if (entry.Value.Length > 0) { + data.Add(entry.Key, entry.Value[0]); + } + } + + if (!data.ContainsKey("toopher_sig")) { + missingKeys.Add("toopher_sig"); + } + if (!data.ContainsKey("timestamp")) { + missingKeys.Add("timestamp"); + } + if (!data.ContainsKey("session_token")) { + missingKeys.Add("session_token"); + } + if (missingKeys.Count() > 0) { + var keys = string.Join(", ", missingKeys.ToArray()); + throw new SignatureValidationError ("Missing required keys: " + keys); + } + + if (data["session_token"] != sessionToken) { + throw new SignatureValidationError ("Session token does not match expected value"); + } + + string maybeSignature = data["toopher_sig"]; + data.Remove("toopher_sig"); + var signatureValid = false; + try { + var computedSig = Signature(consumerSecret, maybeSignature, data); + signatureValid = computedSig == maybeSignature; + } catch (Exception e) { + signatureValid = false; + } + + if (!signatureValid) { + throw new SignatureValidationError ("Computed signature does not match"); + } + + var ttlValid = (int)(GetUnixEpochTimeInSeconds () - ttl) < Int32.Parse(data["timestamp"]); + if (!ttlValid) { + throw new SignatureValidationError ("TTL Expired"); + } + + return data; + } catch (Exception e) { + throw new SignatureValidationError ("Exception while validating toopher signature: " + e); + } + } + + private static string Signature (string secret, string maybeSignature, Dictionary data) + { + Dictionary sortedData = data.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); + string joinedString = string.Join("&", (sortedData.Select(d => d.Key + "=" + d.Value).ToArray())); + + byte[] keyByte = Encoding.UTF8.GetBytes(secret); + byte[] messageBytes = Encoding.UTF8.GetBytes(joinedString); + + using (var hmac = new HMACSHA1(keyByte)) + { + byte[] hashMessage = hmac.ComputeHash(messageBytes); + return Convert.ToBase64String(hashMessage); + } + } + + } + + public class SignatureValidationError: Exception { + public SignatureValidationError (string message): base(message) + { + } + } public class ToopherApi { diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 0934dcc..68ec9a6 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -12,6 +12,15 @@ namespace ToopherDotNetTests [TestFixture()] public class Test { + private static string DEFAULT_BASE_URL = "https://api.toopher.test/v1/"; + private static string TOOPHER_CONSUMER_KEY = "abcdefg"; + private static string TOOPHER_CONSUMER_SECRET = "hijklmnop"; + private static string REQUEST_TOKEN = "s9s7vsb"; + private static long REQUEST_TTL = 100L; + private static string OAUTH_NONCE = "12345678"; + private static DateTime TEST_DATE = new DateTime(1970, 1, 1, 0, 16, 40, 0); + + class MockWebResponse : WebResponse, IHttpWebResponse { HttpStatusCode errorCode; @@ -90,6 +99,11 @@ private ToopherApi getApi () return new ToopherApi ("key", "secret", null, typeof (WebClientMock)); } + private ToopherIframe getToopherIframeApi () + { + return new ToopherIframe (TOOPHER_CONSUMER_KEY, TOOPHER_CONSUMER_SECRET, DEFAULT_BASE_URL, typeof (WebClientMock)); + } + [Test()] public void ToopherVersionTest () { @@ -616,5 +630,101 @@ public void UserTerminalRefreshFromServerTest () Assert.AreEqual (userTerminal.user.name, "userNameChanged"); Assert.IsFalse (userTerminal.user.toopherAuthenticationEnabled); } + public void ValidatePostbackWithGoodSignatureIsSuccessfulTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("session_token", new string[]{REQUEST_TOKEN}); + data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); + try { + Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, 5)); + } catch (Exception) { + Assert.Fail("Valid signture, timestamp, and session token did not return validated data"); + } + } + + [Test] + public void ValidatePostbackWithBadSignatureFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("session_token", new string[]{REQUEST_TOKEN}); + data.Add("toopher_sig", new string[]{"invalid"}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("Computed signature does not match")); + } + + [Test] + public void ValidatePostbackWithInvalidSessionTokenFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("session_token", new string[]{"invalid token"}); + data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("Session token does not match expected value")); + } + + [Test] + public void ValidatePostbackWithExpiredSignatureFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(new DateTime(1970, 2, 1, 0, 16, 40, 0)); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("session_token", new string[]{REQUEST_TOKEN}); + data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("TTL Expired")); + } + + [Test] + public void ValidatePostbackMissingTimestampFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("session_token", new string[]{REQUEST_TOKEN}); + data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("Missing required keys: timestamp")); + } + + [Test] + public void ValidatePostbackMissingSignatureFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("session_token", new string[]{REQUEST_TOKEN}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("Missing required keys: toopher_sig")); + } + + [Test] + public void ValidatePostbackMissingSessionTokenFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("Missing required keys: session_token")); + } } } From cad4e216f1ced476fa2a4fe049d269d8b37695b4 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 11:13:33 -0600 Subject: [PATCH 042/103] Change OAuthTools.Concatenate public to use in ToopherIframe.UrlEncodeParameters --- ToopherDotNet/OAuth/OAuthTools.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ToopherDotNet/OAuth/OAuthTools.cs b/ToopherDotNet/OAuth/OAuthTools.cs index 2c59eb7..cd2c722 100644 --- a/ToopherDotNet/OAuth/OAuthTools.cs +++ b/ToopherDotNet/OAuth/OAuthTools.cs @@ -164,7 +164,7 @@ public static string NormalizeRequestParameters(WebParameterCollection parameter return concatenated; } - private static string Concatenate(ICollection collection, string separator, string spacer) + public static string Concatenate(ICollection collection, string separator, string spacer) { var sb = new StringBuilder(); From 3856d791ec6a7a55fc7b6ab69d789ad396419e76 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 11:32:04 -0600 Subject: [PATCH 043/103] Add OAuthTools.nonceOverride for testing --- ToopherDotNet/OAuth/OAuthTools.cs | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/ToopherDotNet/OAuth/OAuthTools.cs b/ToopherDotNet/OAuth/OAuthTools.cs index cd2c722..0a98b25 100644 --- a/ToopherDotNet/OAuth/OAuthTools.cs +++ b/ToopherDotNet/OAuth/OAuthTools.cs @@ -20,6 +20,12 @@ public static class OAuthTools private static readonly Random _random; private static readonly object _randomLock = new object(); + private static string nonceOverride; + + public static void SetNonceOverride (string nonceOverride) + { + OAuthTools.nonceOverride = nonceOverride; + } #if !SILVERLIGHT private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create(); @@ -49,6 +55,9 @@ static OAuthTools() /// public static string GetNonce() { + if (nonceOverride != null){ + return nonceOverride; + } else { const string chars = (Lower + Digit); var nonce = new char[16]; @@ -60,6 +69,7 @@ public static string GetNonce() } } return new string(nonce); + } } /// From 87c207f3f54af414a08b9a123c601a3d17554a42 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 11:39:35 -0600 Subject: [PATCH 044/103] Add OAuth.dateOverride for testing --- ToopherDotNet/OAuth/OAuthTools.cs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/ToopherDotNet/OAuth/OAuthTools.cs b/ToopherDotNet/OAuth/OAuthTools.cs index 0a98b25..0352e5c 100644 --- a/ToopherDotNet/OAuth/OAuthTools.cs +++ b/ToopherDotNet/OAuth/OAuthTools.cs @@ -27,6 +27,14 @@ public static void SetNonceOverride (string nonceOverride) OAuthTools.nonceOverride = nonceOverride; } + private static DateTime? dateOverride; + + public static void SetDateOverride (DateTime? dateOverride) + { + OAuthTools.dateOverride = dateOverride; + } + + #if !SILVERLIGHT private static readonly RandomNumberGenerator _rng = RandomNumberGenerator.Create(); #endif @@ -79,7 +87,7 @@ public static string GetNonce() /// public static string GetTimestamp() { - return GetTimestamp(DateTime.UtcNow); + return GetTimestamp(dateOverride ?? DateTime.UtcNow); } /// From f48e8ba3c2c76938dbd3b2b943013defcbf945de Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 11:41:54 -0600 Subject: [PATCH 045/103] Add ToopherIframe.GetAuthenticationUrl and GetUserManagementUrl --- ToopherDotNet/ToopherDotNet.cs | 81 ++++++++++++++++++++++-- ToopherDotNetTests/ToopherDotNetTests.cs | 59 +++++++++++++++++ 2 files changed, 133 insertions(+), 7 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 8acb9e1..62731c4 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -24,20 +24,16 @@ public class ToopherIframe private string consumerKey; private string consumerSecret; private Type webClientProxyType; - private static DateTime dateOverride; + private static DateTime? dateOverride; - public static void SetDateOverride (DateTime dateOverride) + public static void SetDateOverride (DateTime? dateOverride) { ToopherIframe.dateOverride = dateOverride; } private static DateTime GetDate () { - if (dateOverride == null) { - return DateTime.UtcNow; - } else { - return dateOverride; - } + return dateOverride ?? DateTime.UtcNow; } private static int GetUnixEpochTimeInSeconds () @@ -62,6 +58,57 @@ public ToopherIframe (string consumerKey, string consumerSecret, string baseUrl } } + public string GetAuthenticationUrl (string userName, string resetEmail, string requestToken, string actionName = "Log In", string requesterMetadata = "None", Dictionary extras = null) + { + NameValueCollection parameters = new NameValueCollection (); + long ttl; + if (extras != null && extras.ContainsKey("ttl")) { + ttl = Convert.ToInt64(extras["ttl"]); + extras.Remove ("ttl"); + } else { + ttl = DEFAULT_TTL; + } + + parameters.Add ("v", IFRAME_VERSION); + parameters.Add ("username", userName); + parameters.Add ("reset_email", resetEmail); + parameters.Add ("session_token", requestToken); + parameters.Add ("action_name", actionName); + parameters.Add ("requester_metadata", requesterMetadata); + parameters.Add ("expires", (GetUnixEpochTimeInSeconds() + ttl).ToString()); + + if (extras != null) { + foreach (KeyValuePair kvp in extras) { + parameters.Add (kvp.Key, kvp.Value); + } + } + return GetOauthUrl (baseUrl + "web/authenticate", parameters); + } + + public string GetUserManagementUrl (string userName, string resetEmail, Dictionary extras = null) + { + NameValueCollection parameters = new NameValueCollection (); + long ttl; + if (extras != null && extras.ContainsKey("ttl")) { + ttl = Convert.ToInt64(extras["ttl"]); + extras.Remove ("ttl"); + } else { + ttl = DEFAULT_TTL; + } + + parameters.Add("v", IFRAME_VERSION); + parameters.Add("username", userName); + parameters.Add("reset_email", resetEmail); + parameters.Add ("expires", (GetUnixEpochTimeInSeconds() + ttl).ToString()); + + if (extras != null) { + foreach (KeyValuePair kvp in extras) { + parameters.Add (kvp.Key, kvp.Value); + } + } + return GetOauthUrl (baseUrl + "web/manage_user", parameters); + } + public Dictionary ValidatePostback (Dictionary extras, string sessionToken, long ttl) { try { @@ -133,6 +180,26 @@ private static string Signature (string secret, string maybeSignature, Dictionar } } + private string GetOauthUrl (string url, NameValueCollection parameters) + { + OAuthRequest client = OAuthRequest.ForRequestToken (consumerKey, consumerSecret); + client.RequestUrl = url; + + string oauthParams = client.GetAuthorizationQuery (parameters); + string requestParams = UrlEncodeParameters (parameters); + return url + "?" + requestParams + "&" + oauthParams; + } + + private string UrlEncodeParameters (NameValueCollection parameters) + { + WebParameterCollection collection = new WebParameterCollection(parameters); + foreach (var parameter in collection) + { + parameter.Value = OAuthTools.UrlEncodeStrict(parameter.Value).Replace("%20", "+"); + } + collection.Sort((x, y) => x.Name.Equals(y.Name) ? x.Value.CompareTo(y.Value) : x.Name.CompareTo(y.Name)); + return OAuthTools.Concatenate(collection, "=", "&"); + } } public class SignatureValidationError: Exception diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 68ec9a6..8427ff2 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -1,6 +1,8 @@ using System; +using OAuth; using System.IO; using System.Net; +using System.Web; using System.Collections.Generic; using System.Collections.Specialized; using Toopher; @@ -92,6 +94,9 @@ public void Init () WebClientMock.ReturnValue = null; WebClientMock.LastRequestMethod = null; WebClientMock.LastRequestData = null; + ToopherIframe.SetDateOverride(null); + OAuthTools.SetNonceOverride(null); + OAuthTools.SetDateOverride(null); } private ToopherApi getApi () @@ -630,6 +635,60 @@ public void UserTerminalRefreshFromServerTest () Assert.AreEqual (userTerminal.user.name, "userNameChanged"); Assert.IsFalse (userTerminal.user.toopherAuthenticationEnabled); } + + [Test] + public void GetAuthenticationUrlTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + OAuthTools.SetNonceOverride (OAUTH_NONCE); + OAuthTools.SetDateOverride (TEST_DATE); + string expected = "https://api.toopher.test/v1/web/authenticate?action_name=Log+In&expires=1300&requester_metadata=None&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=YN%2BkKNTaoypsB37fsjvMS8vsG5A%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var authUrl = api.GetAuthenticationUrl ("jdoe", "jdoe@example.com", REQUEST_TOKEN); + Assert.AreEqual (expected, authUrl); + } + + [Test] + public void GetAuthenticationUrlWithExtrasTest () + { + Dictionary extras = new Dictionary(); + extras.Add("allow_inline_pairing", "false"); + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + OAuthTools.SetNonceOverride (OAUTH_NONCE); + OAuthTools.SetDateOverride (TEST_DATE); + string expected = "https://api.toopher.test/v1/web/authenticate?action_name=it+is+a+test&allow_inline_pairing=false&expires=1300&requester_metadata=None&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=W%2F2dcdsVc7YgdSCZuEo8ViHLlOo%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var authUrl = api.GetAuthenticationUrl ("jdoe", "jdoe@example.com", REQUEST_TOKEN, "it is a test", "None", extras); + Assert.AreEqual (expected, authUrl); + } + + [Test] + public void GetUserManagementUrlTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + OAuthTools.SetNonceOverride (OAUTH_NONCE); + OAuthTools.SetDateOverride (TEST_DATE); + string expected = "https://api.toopher.test/v1/web/manage_user?expires=1300&reset_email=jdoe%40example.com&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=NjwH5yWPE2CCJL8v%2FMNknL%2BeTpE%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var userManagementUrl = api.GetUserManagementUrl ("jdoe", "jdoe@example.com"); + Assert.AreEqual (expected, userManagementUrl); + } + + [Test] + public void GetUserManagementUrlWithExtrasTest () + { + Dictionary extras = new Dictionary(); + extras.Add("ttl", "100"); + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + OAuthTools.SetNonceOverride (OAUTH_NONCE); + OAuthTools.SetDateOverride (TEST_DATE); + string expected = "https://api.toopher.test/v1/web/manage_user?expires=1100&reset_email=jdoe%40example.com&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=sV8qoKnxJ3fxfP6AHNa0eNFxzJs%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var userManagementUrl = api.GetUserManagementUrl ("jdoe", "jdoe@example.com", extras); + Assert.AreEqual (expected, userManagementUrl); + } + + [Test] public void ValidatePostbackWithGoodSignatureIsSuccessfulTest () { var api = getToopherIframeApi(); From 5e64ffa96ae9d3e05cfb7f81fca8cffac5002bd9 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 16:15:52 -0600 Subject: [PATCH 046/103] Remove old duplicate methods and tests --- ToopherDotNet/ToopherDotNet.cs | 58 ------------------------ ToopherDotNetTests/ToopherDotNetTests.cs | 9 ---- 2 files changed, 67 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 62731c4..d6c1b19 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -315,64 +315,6 @@ public AuthenticationRequest Authenticate (string pairingIdOrUsername, string te return new AuthenticationRequest (json, this); } - /// - /// Create a named terminal in the Toopher API for the (userName, terminalIdentifier) combination - /// - /// Name of the user - /// User-assigned "friendly" terminal name - /// Unique terminal identifier for this terminal. Does not need to be human-readable. - /// Thrown when there is a problem contacting the Toopher API - public void CreateUserTerminal (string userName, string terminalName, string terminalIdentifier) - { - string endpoint = "user_terminals/create"; - NameValueCollection parameters = new NameValueCollection(); - parameters["user_name"] = userName; - parameters["name"] = terminalName; - parameters["name_extra"] = terminalIdentifier; - advanced.raw.post (endpoint, parameters); - } - - /// - /// Enable or Disable Toopher Authentication for an individual user. If the user is - /// disabled, future attempts to authenticate the user with Toopher will return - /// a UserDisabledError - /// - /// Name of the user to modify - /// True if the user should be authenticated with Toopher - /// Thrown when there is a problem contacting the Toopher API - public void SetToopherEnabledForUser (string userName, bool toopherEnabled) - { - string searchEndpoint = "users"; - NameValueCollection parameters = new NameValueCollection (); - parameters["user_name"] = userName; - - JsonArray jArr = advanced.raw.getArray (searchEndpoint, parameters); - if (jArr.Count > 1) { - throw new RequestError ("Multiple users with name = " + userName); - } - if (jArr.Count == 0) { - throw new RequestError ("No users with name = " + userName); - } - - string userId = (string)((JsonObject)jArr[0])["id"]; - - string updateEndpoint = "users/" + userId; - parameters = new NameValueCollection (); - parameters["disable_toopher_auth"] = toopherEnabled ? "false" : "true"; - advanced.raw.post (updateEndpoint, parameters); - } - - public string GetPairingResetLink (string pairingId, string securityQuestion = null, string securityAnswer = null) - { - string endpoint = "pairings/" + pairingId + "/generate_reset_link"; - NameValueCollection parameters = new NameValueCollection (); - parameters["security_question"] = securityQuestion; - parameters["security_answer"] = securityAnswer; - - JsonObject pairingResetLink = advanced.raw.post (endpoint, parameters); - return (string)pairingResetLink["url"]; - } - public class AdvancedApiUsageFactory { public ToopherApi.AdvancedApiUsageFactory.Pairings pairings; diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 8427ff2..d5cbbeb 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -445,15 +445,6 @@ public void AdvancedUserTerminalsCreateWithParamsTest () Assert.IsInstanceOf (userTerminal.user); } - [Test] - public void GeneratePairingLinkTest () - { - var api = getApi (); - WebClientMock.ReturnValue = @"{""reset_authorization"":""abcde"", ""url"":""http://testonly/pairings/1/reset?reset_authorization=abcde""}}"; - string pairingResetLink = api.GetPairingResetLink ("1"); - Assert.AreEqual (pairingResetLink, "http://testonly/pairings/1/reset?reset_authorization=abcde"); - } - [Test] public void GenerateAdvancedApiUsageFactory () { From 2eb80fc3812d1d66162658691ac8ca31f1fbc78b Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 16:38:20 -0600 Subject: [PATCH 047/103] Split tests into TestFixtures for each class --- ToopherDotNetTests/ToopherDotNetTests.cs | 598 ++++++++++++----------- 1 file changed, 322 insertions(+), 276 deletions(-) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index d5cbbeb..73e8564 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -12,16 +12,11 @@ namespace ToopherDotNetTests { [TestFixture()] - public class Test + public class TestBase { - private static string DEFAULT_BASE_URL = "https://api.toopher.test/v1/"; - private static string TOOPHER_CONSUMER_KEY = "abcdefg"; - private static string TOOPHER_CONSUMER_SECRET = "hijklmnop"; - private static string REQUEST_TOKEN = "s9s7vsb"; - private static long REQUEST_TTL = 100L; - private static string OAUTH_NONCE = "12345678"; - private static DateTime TEST_DATE = new DateTime(1970, 1, 1, 0, 16, 40, 0); - + public const string DEFAULT_BASE_URL = "https://api.toopher.test/v1/"; + public const string TOOPHER_CONSUMER_KEY = "abcdefg"; + public const string TOOPHER_CONSUMER_SECRET = "hijklmnop"; class MockWebResponse : WebResponse, IHttpWebResponse { @@ -49,12 +44,12 @@ public string StatusDescription get { return ""; } } } - WebException makeError (HttpStatusCode errorCode, string responseBody) + public WebException makeError (HttpStatusCode errorCode, string responseBody) { WebResponse response = new MockWebResponse (errorCode, responseBody); return new WebException ("", null, WebExceptionStatus.ProtocolError, response); } - class WebClientMock : WebClientProxy + public class WebClientMock : WebClientProxy { static public String LastRequestMethod { get; set; } static public NameValueCollection LastRequestData { get; set; } @@ -88,7 +83,7 @@ override public string DownloadString (string requestUri) } [SetUp] - public void Init () + public virtual void Init () { WebClientMock.ReturnException = null; WebClientMock.ReturnValue = null; @@ -99,16 +94,186 @@ public void Init () OAuthTools.SetDateOverride(null); } - private ToopherApi getApi () + public ToopherApi getApi () { return new ToopherApi ("key", "secret", null, typeof (WebClientMock)); } + } + + [TestFixture()] + public class ToopherIframeTests : TestBase + { + private const string REQUEST_TOKEN = "s9s7vsb"; + private const string OAUTH_NONCE = "12345678"; + private DateTime TEST_DATE = new DateTime(1970, 1, 1, 0, 16, 40, 0); + + [SetUp] + public override void Init () + { + ToopherIframe.SetDateOverride(null); + OAuthTools.SetNonceOverride(null); + OAuthTools.SetDateOverride(null); + } private ToopherIframe getToopherIframeApi () { return new ToopherIframe (TOOPHER_CONSUMER_KEY, TOOPHER_CONSUMER_SECRET, DEFAULT_BASE_URL, typeof (WebClientMock)); } + [Test] + public void GetAuthenticationUrlTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + OAuthTools.SetNonceOverride (OAUTH_NONCE); + OAuthTools.SetDateOverride (TEST_DATE); + string expected = "https://api.toopher.test/v1/web/authenticate?action_name=Log+In&expires=1300&requester_metadata=None&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=YN%2BkKNTaoypsB37fsjvMS8vsG5A%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var authUrl = api.GetAuthenticationUrl ("jdoe", "jdoe@example.com", REQUEST_TOKEN); + Assert.AreEqual (expected, authUrl); + } + + [Test] + public void GetAuthenticationUrlWithExtrasTest () + { + Dictionary extras = new Dictionary(); + extras.Add("allow_inline_pairing", "false"); + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + OAuthTools.SetNonceOverride (OAUTH_NONCE); + OAuthTools.SetDateOverride (TEST_DATE); + string expected = "https://api.toopher.test/v1/web/authenticate?action_name=it+is+a+test&allow_inline_pairing=false&expires=1300&requester_metadata=None&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=W%2F2dcdsVc7YgdSCZuEo8ViHLlOo%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var authUrl = api.GetAuthenticationUrl ("jdoe", "jdoe@example.com", REQUEST_TOKEN, "it is a test", "None", extras); + Assert.AreEqual (expected, authUrl); + } + + [Test] + public void GetUserManagementUrlTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + OAuthTools.SetNonceOverride (OAUTH_NONCE); + OAuthTools.SetDateOverride (TEST_DATE); + string expected = "https://api.toopher.test/v1/web/manage_user?expires=1300&reset_email=jdoe%40example.com&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=NjwH5yWPE2CCJL8v%2FMNknL%2BeTpE%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var userManagementUrl = api.GetUserManagementUrl ("jdoe", "jdoe@example.com"); + Assert.AreEqual (expected, userManagementUrl); + } + + [Test] + public void GetUserManagementUrlWithExtrasTest () + { + Dictionary extras = new Dictionary(); + extras.Add("ttl", "100"); + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + OAuthTools.SetNonceOverride (OAUTH_NONCE); + OAuthTools.SetDateOverride (TEST_DATE); + string expected = "https://api.toopher.test/v1/web/manage_user?expires=1100&reset_email=jdoe%40example.com&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=sV8qoKnxJ3fxfP6AHNa0eNFxzJs%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var userManagementUrl = api.GetUserManagementUrl ("jdoe", "jdoe@example.com", extras); + Assert.AreEqual (expected, userManagementUrl); + } + + [Test] + public void ValidatePostbackWithGoodSignatureIsSuccessfulTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("session_token", new string[]{REQUEST_TOKEN}); + data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); + try { + Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, 5)); + } catch (Exception) { + Assert.Fail("Valid signture, timestamp, and session token did not return validated data"); + } + } + + [Test] + public void ValidatePostbackWithBadSignatureFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("session_token", new string[]{REQUEST_TOKEN}); + data.Add("toopher_sig", new string[]{"invalid"}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("Computed signature does not match")); + } + + [Test] + public void ValidatePostbackWithExpiredSignatureFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(new DateTime(1970, 2, 1, 0, 16, 40, 0)); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("session_token", new string[]{REQUEST_TOKEN}); + data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("TTL Expired")); + } + + [Test] + public void ValidatePostbackWithInvalidSessionTokenFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("session_token", new string[]{"invalid token"}); + data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("Session token does not match expected value")); + } + + [Test] + public void ValidatePostbackMissingTimestampFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("session_token", new string[]{REQUEST_TOKEN}); + data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("Missing required keys: timestamp")); + } + + [Test] + public void ValidatePostbackMissingSignatureFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("session_token", new string[]{REQUEST_TOKEN}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("Missing required keys: toopher_sig")); + } + + [Test] + public void ValidatePostbackMissingSessionTokenFailsTest () + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary (); + data.Add("foo", new string[]{"bar"}); + data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); + data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.That(ex.Message, Is.StringContaining("Missing required keys: session_token")); + } + } + + [TestFixture()] + public class ToopherApiTests : TestBase + { [Test()] public void ToopherVersionTest () { @@ -163,6 +328,17 @@ public void CreateQrPairingTest () Assert.AreEqual (pairing.id, "1"); } + [Test] + public void ArbitraryParametersOnPairTest () + { + var api = getApi (); + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; + Pairing pairing = api.Pair ("awkward turtle", "some user", extras: new Dictionary(){{"test_param", "42"}}); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); + Assert.AreEqual (pairing.id, "1"); + } + [Test] public void AuthenticateWithPairingIdTest () { @@ -189,17 +365,6 @@ public void AuthenticateWithUsernameAndExtrasTest () Assert.AreEqual (auth.id, "1"); } - [Test] - public void ArbitraryParametersOnPairTest () - { - var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; - Pairing pairing = api.Pair ("awkward turtle", "some user", extras: new Dictionary(){{"test_param", "42"}}); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); - Assert.AreEqual (pairing.id, "1"); - } - [Test] public void ArbitraryParamtersOnAuthenticateTest () { @@ -212,96 +377,73 @@ public void ArbitraryParamtersOnAuthenticateTest () } [Test] - public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest () + public void GenerateAdvancedApiUsageFactory () { - var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""random_key"":""84""}"; - Pairing pairing = api.advanced.pairings.GetById ("1"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (pairing.id, "1"); - Assert.AreEqual (pairing.user.name, "some user"); - Assert.AreEqual (pairing.user.id, "1"); - Assert.IsTrue (pairing.enabled); - Assert.AreEqual (pairing["random_key"], "84"); + var api = getApi(); + Assert.IsInstanceOf (api.advanced); } [Test] - public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest () + public void GenerateAdvancedPairings () { - var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}, ""random_key"":""84""}"; - AuthenticationRequest auth = api.advanced.authenticationRequests.GetById ("1"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (auth.id, "1"); - Assert.IsFalse (auth.pending); - Assert.IsTrue (auth.granted); - Assert.AreEqual (auth.reason, "its a test"); - Assert.AreEqual (auth.terminal.id, "1"); - Assert.AreEqual (auth.terminal.name, "test terminal"); - Assert.AreEqual (auth["random_key"], "84"); + var api = getApi(); + Assert.IsInstanceOf (api.advanced.pairings); } [Test] - public void AccessArbitraryKeysInAdvancedUsersGetByIdTest () + public void GenerateAdvancedRaw () { - var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true, ""random_key"":""84""}"; - User user = api.advanced.users.GetById ("1"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "username"); - Assert.IsTrue (user.toopherAuthenticationEnabled); - Assert.AreEqual (user["random_key"], "84"); + var api = getApi(); + Assert.IsInstanceOf (api.advanced.raw); } [Test] - [ExpectedException(typeof(UserDisabledError))] - public void DisabledUserRaisesCorrectErrorTest () + public void GenerateAdvancedAuthenticationRequests () { - var api = getApi (); - WebClientMock.ReturnException = makeError((HttpStatusCode)409, - @"{""error_code"":704, ""error_message"":""The specified user has disabled Toopher authentication.""}"); - api.Authenticate ("some disabled user", "some random string"); + var api = getApi(); + Assert.IsInstanceOf (api.advanced.authenticationRequests); } [Test] - [ExpectedException (typeof (UserUnknownError))] - public void UnknownUserRaisesCorrectErrorTest () + public void GenerateAdvancedUsers () { - var api = getApi (); - WebClientMock.ReturnException = makeError ((HttpStatusCode)409, - @"{""error_code"":705, ""error_message"":""No matching user exists.""}"); - api.Authenticate ("some unknown user", "some random string"); + var api = getApi(); + Assert.IsInstanceOf (api.advanced.users); } [Test] - [ExpectedException (typeof (TerminalUnknownError))] - public void UnknownTerminalRaisesCorrectErrorTest () + public void GenerateAdvancedUserTerminals () { - var api = getApi (); - WebClientMock.ReturnException = makeError ((HttpStatusCode)409, - @"{""error_code"":706, ""error_message"":""No matching terminal exists.""}"); - api.Authenticate ("some unknown user", "some random string"); + var api = getApi(); + Assert.IsInstanceOf (api.advanced.userTerminals); } [Test] - [ExpectedException (typeof (PairingDeactivatedError))] - public void DeactivatedPairingRaisesCorrectErrorTest () + public void AdvancedPairingsGetByIdTest () { var api = getApi (); - WebClientMock.ReturnException = makeError ((HttpStatusCode)409, - @"{""error_code"":601, ""error_message"":""This pairing has been deactivated.""}"); - api.Authenticate ("some disabled user", "some random string"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; + Pairing pairing = api.advanced.pairings.GetById ("1"); + Assert.IsInstanceOf (pairing); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual (pairing.id, "1"); + Assert.AreEqual (pairing.user.name, "some user"); + Assert.AreEqual (pairing.user.id, "1"); + Assert.IsTrue (pairing.enabled); } [Test] - [ExpectedException (typeof (PairingDeactivatedError))] - public void UnauthorizedPairingRaisesCorrectErrorTest () + public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest () { var api = getApi (); - WebClientMock.ReturnException = makeError ((HttpStatusCode)409, - @"{""error_code"":601, ""error_message"":""This pairing has not been authorized to authenticate.""}"); - api.Authenticate ("some unauthorized user", "some random string"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""random_key"":""84""}"; + Pairing pairing = api.advanced.pairings.GetById ("1"); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual (pairing.id, "1"); + Assert.AreEqual (pairing.user.name, "some user"); + Assert.AreEqual (pairing.user.id, "1"); + Assert.IsTrue (pairing.enabled); + Assert.AreEqual (pairing["random_key"], "84"); } [Test] @@ -320,31 +462,19 @@ public void AdvancedAuthenticationRequestsGetByIdTest () } [Test] - public void AuthenticationRequestGrantWithOtpTest () + public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest () { var api = getApi (); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":true, ""granted"":false, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; - AuthenticationRequest auth = new AuthenticationRequest (response, api); - auth.GrantWithOtp ("123456"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["otp"], "123456"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}, ""random_key"":""84""}"; + AuthenticationRequest auth = api.advanced.authenticationRequests.GetById ("1"); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (auth.id, "1"); + Assert.IsFalse (auth.pending); Assert.IsTrue (auth.granted); - } - - [Test] - public void AdvancedPairingsGetByIdTest () - { - var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; - Pairing pairing = api.advanced.pairings.GetById ("1"); - Assert.IsInstanceOf (pairing); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (pairing.id, "1"); - Assert.AreEqual (pairing.user.name, "some user"); - Assert.AreEqual (pairing.user.id, "1"); - Assert.IsTrue (pairing.enabled); + Assert.AreEqual (auth.reason, "its a test"); + Assert.AreEqual (auth.terminal.id, "1"); + Assert.AreEqual (auth.terminal.name, "test terminal"); + Assert.AreEqual (auth["random_key"], "84"); } [Test] @@ -360,6 +490,19 @@ public void AdvancedUsersGetByIdTest () Assert.IsTrue (user.toopherAuthenticationEnabled); } + [Test] + public void AccessArbitraryKeysInAdvancedUsersGetByIdTest () + { + var api = getApi (); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true, ""random_key"":""84""}"; + User user = api.advanced.users.GetById ("1"); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual (user.id, "1"); + Assert.AreEqual (user.name, "username"); + Assert.IsTrue (user.toopherAuthenticationEnabled); + Assert.AreEqual (user["random_key"], "84"); + } + [Test] public void AdvancedUsersGetByNameTest () { @@ -416,6 +559,19 @@ public void AdvancedUserTerminalsGetByIdTest () Assert.IsInstanceOf (userTerminal.user); } + [Test] + public void AccessArbitraryKeysInAdvancedUserTerminalsGetByIdTest () + { + var api = getApi (); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"",""random_key"":""84"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; + UserTerminal userTerminal = api.advanced.userTerminals.GetById ("1"); + Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual (userTerminal.id, "1"); + Assert.AreEqual (userTerminal.name, "userTerminalName"); + Assert.IsTrue (userTerminal.user.toopherAuthenticationEnabled); + Assert.AreEqual (userTerminal["random_key"], "84"); + } + [Test] public void AdvancedUserTerminalsCreateTest () { @@ -446,47 +602,59 @@ public void AdvancedUserTerminalsCreateWithParamsTest () } [Test] - public void GenerateAdvancedApiUsageFactory () - { - var api = getApi(); - Assert.IsInstanceOf (api.advanced); - } - - [Test] - public void GenerateAdvancedPairings () + [ExpectedException(typeof(UserDisabledError))] + public void DisabledUserRaisesCorrectErrorTest () { - var api = getApi(); - Assert.IsInstanceOf (api.advanced.pairings); + var api = getApi (); + WebClientMock.ReturnException = makeError((HttpStatusCode)409, + @"{""error_code"":704, ""error_message"":""The specified user has disabled Toopher authentication.""}"); + api.Authenticate ("some disabled user", "some random string"); } [Test] - public void GenerateAdvancedRaw () + [ExpectedException (typeof (UserUnknownError))] + public void UnknownUserRaisesCorrectErrorTest () { - var api = getApi(); - Assert.IsInstanceOf (api.advanced.raw); + var api = getApi (); + WebClientMock.ReturnException = makeError ((HttpStatusCode)409, + @"{""error_code"":705, ""error_message"":""No matching user exists.""}"); + api.Authenticate ("some unknown user", "some random string"); } [Test] - public void GenerateAdvancedAuthenticationRequests () + [ExpectedException (typeof (TerminalUnknownError))] + public void UnknownTerminalRaisesCorrectErrorTest () { - var api = getApi(); - Assert.IsInstanceOf (api.advanced.authenticationRequests); + var api = getApi (); + WebClientMock.ReturnException = makeError ((HttpStatusCode)409, + @"{""error_code"":706, ""error_message"":""No matching terminal exists.""}"); + api.Authenticate ("some unknown user", "some random string"); } [Test] - public void GenerateAdvancedUsers () + [ExpectedException (typeof (PairingDeactivatedError))] + public void DeactivatedPairingRaisesCorrectErrorTest () { - var api = getApi(); - Assert.IsInstanceOf (api.advanced.users); + var api = getApi (); + WebClientMock.ReturnException = makeError ((HttpStatusCode)409, + @"{""error_code"":601, ""error_message"":""This pairing has been deactivated.""}"); + api.Authenticate ("some disabled user", "some random string"); } [Test] - public void GenerateAdvancedUserTerminals () + [ExpectedException (typeof (PairingDeactivatedError))] + public void UnauthorizedPairingRaisesCorrectErrorTest () { - var api = getApi(); - Assert.IsInstanceOf (api.advanced.userTerminals); + var api = getApi (); + WebClientMock.ReturnException = makeError ((HttpStatusCode)409, + @"{""error_code"":601, ""error_message"":""This pairing has not been authorized to authenticate.""}"); + api.Authenticate ("some unauthorized user", "some random string"); } + } + [TestFixture()] + public class PairingTests : TestBase + { [Test] public void PairingRefreshFromServerTest () { @@ -538,7 +706,12 @@ public void PairingGetQrCodeImage () pairing.GetQrCodeImage (); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); } + } + + [TestFixture()] + public class AuthenticationRequestTests : TestBase + { [Test] public void AuthenticationRequestRefreshFromServerTest () { @@ -556,6 +729,24 @@ public void AuthenticationRequestRefreshFromServerTest () Assert.AreEqual (auth.action.name, "actionName CHANGED"); } + [Test] + public void AuthenticationRequestGrantWithOtpTest () + { + var api = getApi (); + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":true, ""granted"":false, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"); + WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; + AuthenticationRequest auth = new AuthenticationRequest (response, api); + auth.GrantWithOtp ("123456"); + Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual (WebClientMock.LastRequestData["otp"], "123456"); + Assert.AreEqual (auth.id, "1"); + Assert.IsTrue (auth.granted); + } + } + + [TestFixture()] + public class UserTests : TestBase + { [Test] public void UserRefreshFromServerTest () { @@ -610,7 +801,12 @@ public void UserResetTest () Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["name"], "userName"); } + } + + [TestFixture()] + public class UserTerminalTests : TestBase + { [Test] public void UserTerminalRefreshFromServerTest () { @@ -626,155 +822,5 @@ public void UserTerminalRefreshFromServerTest () Assert.AreEqual (userTerminal.user.name, "userNameChanged"); Assert.IsFalse (userTerminal.user.toopherAuthenticationEnabled); } - - [Test] - public void GetAuthenticationUrlTest () - { - var api = getToopherIframeApi(); - ToopherIframe.SetDateOverride(TEST_DATE); - OAuthTools.SetNonceOverride (OAUTH_NONCE); - OAuthTools.SetDateOverride (TEST_DATE); - string expected = "https://api.toopher.test/v1/web/authenticate?action_name=Log+In&expires=1300&requester_metadata=None&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=YN%2BkKNTaoypsB37fsjvMS8vsG5A%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; - var authUrl = api.GetAuthenticationUrl ("jdoe", "jdoe@example.com", REQUEST_TOKEN); - Assert.AreEqual (expected, authUrl); - } - - [Test] - public void GetAuthenticationUrlWithExtrasTest () - { - Dictionary extras = new Dictionary(); - extras.Add("allow_inline_pairing", "false"); - var api = getToopherIframeApi(); - ToopherIframe.SetDateOverride(TEST_DATE); - OAuthTools.SetNonceOverride (OAUTH_NONCE); - OAuthTools.SetDateOverride (TEST_DATE); - string expected = "https://api.toopher.test/v1/web/authenticate?action_name=it+is+a+test&allow_inline_pairing=false&expires=1300&requester_metadata=None&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=W%2F2dcdsVc7YgdSCZuEo8ViHLlOo%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; - var authUrl = api.GetAuthenticationUrl ("jdoe", "jdoe@example.com", REQUEST_TOKEN, "it is a test", "None", extras); - Assert.AreEqual (expected, authUrl); - } - - [Test] - public void GetUserManagementUrlTest () - { - var api = getToopherIframeApi(); - ToopherIframe.SetDateOverride(TEST_DATE); - OAuthTools.SetNonceOverride (OAUTH_NONCE); - OAuthTools.SetDateOverride (TEST_DATE); - string expected = "https://api.toopher.test/v1/web/manage_user?expires=1300&reset_email=jdoe%40example.com&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=NjwH5yWPE2CCJL8v%2FMNknL%2BeTpE%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; - var userManagementUrl = api.GetUserManagementUrl ("jdoe", "jdoe@example.com"); - Assert.AreEqual (expected, userManagementUrl); - } - - [Test] - public void GetUserManagementUrlWithExtrasTest () - { - Dictionary extras = new Dictionary(); - extras.Add("ttl", "100"); - var api = getToopherIframeApi(); - ToopherIframe.SetDateOverride(TEST_DATE); - OAuthTools.SetNonceOverride (OAUTH_NONCE); - OAuthTools.SetDateOverride (TEST_DATE); - string expected = "https://api.toopher.test/v1/web/manage_user?expires=1100&reset_email=jdoe%40example.com&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=sV8qoKnxJ3fxfP6AHNa0eNFxzJs%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; - var userManagementUrl = api.GetUserManagementUrl ("jdoe", "jdoe@example.com", extras); - Assert.AreEqual (expected, userManagementUrl); - } - - [Test] - public void ValidatePostbackWithGoodSignatureIsSuccessfulTest () - { - var api = getToopherIframeApi(); - ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("session_token", new string[]{REQUEST_TOKEN}); - data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - try { - Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, 5)); - } catch (Exception) { - Assert.Fail("Valid signture, timestamp, and session token did not return validated data"); - } - } - - [Test] - public void ValidatePostbackWithBadSignatureFailsTest () - { - var api = getToopherIframeApi(); - ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("session_token", new string[]{REQUEST_TOKEN}); - data.Add("toopher_sig", new string[]{"invalid"}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); - Assert.That(ex.Message, Is.StringContaining("Computed signature does not match")); - } - - [Test] - public void ValidatePostbackWithInvalidSessionTokenFailsTest () - { - var api = getToopherIframeApi(); - ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("session_token", new string[]{"invalid token"}); - data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); - Assert.That(ex.Message, Is.StringContaining("Session token does not match expected value")); - } - - [Test] - public void ValidatePostbackWithExpiredSignatureFailsTest () - { - var api = getToopherIframeApi(); - ToopherIframe.SetDateOverride(new DateTime(1970, 2, 1, 0, 16, 40, 0)); - Dictionary data = new Dictionary (); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("session_token", new string[]{REQUEST_TOKEN}); - data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); - Assert.That(ex.Message, Is.StringContaining("TTL Expired")); - } - - [Test] - public void ValidatePostbackMissingTimestampFailsTest () - { - var api = getToopherIframeApi(); - ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); - data.Add("foo", new string[]{"bar"}); - data.Add("session_token", new string[]{REQUEST_TOKEN}); - data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); - Assert.That(ex.Message, Is.StringContaining("Missing required keys: timestamp")); - } - - [Test] - public void ValidatePostbackMissingSignatureFailsTest () - { - var api = getToopherIframeApi(); - ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("session_token", new string[]{REQUEST_TOKEN}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); - Assert.That(ex.Message, Is.StringContaining("Missing required keys: toopher_sig")); - } - - [Test] - public void ValidatePostbackMissingSessionTokenFailsTest () - { - var api = getToopherIframeApi(); - ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); - Assert.That(ex.Message, Is.StringContaining("Missing required keys: session_token")); - } } } From 3dfa195a7521be22a8ced6693312e03c0945d3e6 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 16:54:19 -0600 Subject: [PATCH 048/103] Add constants for testing --- ToopherDotNetTests/ToopherDotNetTests.cs | 106 ++++++++++++----------- 1 file changed, 54 insertions(+), 52 deletions(-) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 73e8564..1fc20c2 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -18,6 +18,11 @@ public class TestBase public const string TOOPHER_CONSUMER_KEY = "abcdefg"; public const string TOOPHER_CONSUMER_SECRET = "hijklmnop"; + public const string PAIRING_RESPONSE = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; + public const string AUTH_REQUEST_RESPONSE = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; + public const string USER_RESPONSE = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}"; + public const string USER_TERMINAL_RESPONSE = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; + class MockWebResponse : WebResponse, IHttpWebResponse { HttpStatusCode errorCode; @@ -297,7 +302,7 @@ public void ToopherBaseUrlTest () public void CreatePairingWithPhraseTest () { var api = getApi(); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; + WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair ("some user", "awkward turtle"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["pairing_phrase"], "awkward turtle"); @@ -309,7 +314,7 @@ public void CreatePairingWithPhraseTest () public void CreateSmsPairingTest () { var api = getApi(); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; + WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair ("some user", "555-555-5555"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["phone_number"], "555-555-5555"); @@ -321,7 +326,7 @@ public void CreateSmsPairingTest () public void CreateQrPairingTest () { var api = getApi(); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; + WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair ("some user"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some user"); @@ -332,7 +337,7 @@ public void CreateQrPairingTest () public void ArbitraryParametersOnPairTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; + WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair ("awkward turtle", "some user", extras: new Dictionary(){{"test_param", "42"}}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); @@ -356,7 +361,7 @@ public void AuthenticateWithPairingIdTest () public void AuthenticateWithUsernameAndExtrasTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; + WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.Authenticate ("some other user", "requester specified id", extras: new Dictionary() {{ "random_key" , "42" }}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some other user"); @@ -369,7 +374,7 @@ public void AuthenticateWithUsernameAndExtrasTest () public void ArbitraryParamtersOnAuthenticateTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1,""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; + WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.Authenticate ("1", "test terminal", extras: new Dictionary () { { "test_param", "42" } }); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); @@ -422,7 +427,7 @@ public void GenerateAdvancedUserTerminals () public void AdvancedPairingsGetByIdTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}"; + WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.advanced.pairings.GetById ("1"); Assert.IsInstanceOf (pairing); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); @@ -450,7 +455,7 @@ public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest () public void AdvancedAuthenticationRequestsGetByIdTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; + WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.advanced.authenticationRequests.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (auth.id, "1"); @@ -481,12 +486,12 @@ public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest () public void AdvancedUsersGetByIdTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}"; + WebClientMock.ReturnValue = USER_RESPONSE; User user = api.advanced.users.GetById ("1"); Assert.IsInstanceOf (user); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "username"); + Assert.AreEqual (user.name, "userName"); Assert.IsTrue (user.toopherAuthenticationEnabled); } @@ -494,11 +499,11 @@ public void AdvancedUsersGetByIdTest () public void AccessArbitraryKeysInAdvancedUsersGetByIdTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true, ""random_key"":""84""}"; + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true, ""random_key"":""84""}"; User user = api.advanced.users.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "username"); + Assert.AreEqual (user.name, "userName"); Assert.IsTrue (user.toopherAuthenticationEnabled); Assert.AreEqual (user["random_key"], "84"); } @@ -507,12 +512,12 @@ public void AccessArbitraryKeysInAdvancedUsersGetByIdTest () public void AdvancedUsersGetByNameTest () { var api = getApi (); - WebClientMock.ReturnValueArray = @"[{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}]"; - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}"; - User user = api.advanced.users.GetByName("username"); + WebClientMock.ReturnValueArray = @"[{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}]"; + WebClientMock.ReturnValue = USER_RESPONSE; + User user = api.advanced.users.GetByName("userName"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "username"); + Assert.AreEqual (user.name, "userName"); Assert.IsTrue (user.toopherAuthenticationEnabled); } @@ -520,12 +525,12 @@ public void AdvancedUsersGetByNameTest () public void AdvancedUsersCreateTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}"; - User user = api.advanced.users.Create ("username"); + WebClientMock.ReturnValue = USER_RESPONSE; + User user = api.advanced.users.Create ("userName"); Assert.IsInstanceOf (user); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "username"); + Assert.AreEqual (user.name, "userName"); Assert.IsTrue (user.toopherAuthenticationEnabled); } @@ -533,15 +538,15 @@ public void AdvancedUsersCreateTest () public void AdvancedUsersCreateWithParamsTest () { var api = getApi(); - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}"; + WebClientMock.ReturnValue = USER_RESPONSE; NameValueCollection parameters = new NameValueCollection(); parameters.Add ("foo", "bar"); - User user = api.advanced.users.Create ("username", parameters); + User user = api.advanced.users.Create ("userName", parameters); Assert.IsInstanceOf (user); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["foo"], "bar"); Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "username"); + Assert.AreEqual (user.name, "userName"); Assert.IsTrue (user.toopherAuthenticationEnabled); } @@ -549,7 +554,7 @@ public void AdvancedUsersCreateWithParamsTest () public void AdvancedUserTerminalsGetByIdTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; + WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; UserTerminal userTerminal = api.advanced.userTerminals.GetById ("1"); Assert.IsInstanceOf (userTerminal); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); @@ -576,7 +581,7 @@ public void AccessArbitraryKeysInAdvancedUserTerminalsGetByIdTest () public void AdvancedUserTerminalsCreateTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; + WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; UserTerminal userTerminal = api.advanced.userTerminals.Create ("userName", "userTerminalName", "requesterSpecifiedId"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (userTerminal.id, "1"); @@ -589,7 +594,7 @@ public void AdvancedUserTerminalsCreateTest () public void AdvancedUserTerminalsCreateWithParamsTest () { var api = getApi (); - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; + WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; NameValueCollection parameters = new NameValueCollection(); parameters.Add ("foo", "bar"); UserTerminal userTerminal = api.advanced.userTerminals.Create ("userName", "userTerminalName", "requesterSpecifiedId", parameters); @@ -655,13 +660,14 @@ public void UnauthorizedPairingRaisesCorrectErrorTest () [TestFixture()] public class PairingTests : TestBase { + private IDictionary PAIRING_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); + [Test] public void PairingRefreshFromServerTest () { var api = getApi(); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""enabled"":false, ""user"":{""id"":""1"",""name"":""userNameChanged"", ""toopher_authentication_enabled"":true}}"; - Pairing pairing = new Pairing (response, api); + Pairing pairing = new Pairing (PAIRING_DICT, api); pairing.RefreshFromServer (); Assert.IsInstanceOf (pairing); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); @@ -675,10 +681,9 @@ public void PairingRefreshFromServerTest () public void PairingGetResetLinkTest () { var api = getApi(); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); string link = "http://api.toopher.test/v1/pairings/1/reset?reset_authorization=abcde"; WebClientMock.ReturnValue = @"{""url"":""" + link + @"""}"; - Pairing pairing = new Pairing (response, api); + Pairing pairing = new Pairing (PAIRING_DICT, api); string returnedLink = pairing.GetResetLink (); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (returnedLink, link); @@ -688,9 +693,8 @@ public void PairingGetResetLinkTest () public void PairingEmailResetLinkTest () { var api = getApi(); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); WebClientMock.ReturnValue = @"{}"; - Pairing pairing = new Pairing (response, api); + Pairing pairing = new Pairing (PAIRING_DICT, api); pairing.EmailResetLink ("test@test.com"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["reset_email"], "test@test.com"); @@ -700,9 +704,8 @@ public void PairingEmailResetLinkTest () public void PairingGetQrCodeImage () { var api = getApi(); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); WebClientMock.ReturnValue = @"{}"; - Pairing pairing = new Pairing (response, api); + Pairing pairing = new Pairing (PAIRING_DICT, api); pairing.GetQrCodeImage (); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); } @@ -712,13 +715,14 @@ public void PairingGetQrCodeImage () [TestFixture()] public class AuthenticationRequestTests : TestBase { + private IDictionary AUTH_REQUEST_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":true, ""granted"":false, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"); + [Test] public void AuthenticationRequestRefreshFromServerTest () { var api = getApi(); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""granted"":true, ""automated"":false, ""reason_code"":2, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal CHANGED"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user CHANGED"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName CHANGED""}}"; - AuthenticationRequest auth = new AuthenticationRequest (response, api); + AuthenticationRequest auth = new AuthenticationRequest (AUTH_REQUEST_DICT, api); auth.RefreshFromServer (); Assert.IsInstanceOf (auth); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); @@ -733,9 +737,8 @@ public void AuthenticationRequestRefreshFromServerTest () public void AuthenticationRequestGrantWithOtpTest () { var api = getApi (); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":true, ""granted"":false, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"); - WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; - AuthenticationRequest auth = new AuthenticationRequest (response, api); + WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; + AuthenticationRequest auth = new AuthenticationRequest (AUTH_REQUEST_DICT, api); auth.GrantWithOtp ("123456"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["otp"], "123456"); @@ -747,28 +750,28 @@ public void AuthenticationRequestGrantWithOtpTest () [TestFixture()] public class UserTests : TestBase { + private IDictionary USER_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}"); + [Test] public void UserRefreshFromServerTest () { var api = getApi(); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":false}"); - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userNameChanged"", ""toopher_authentication_enabled"":true}"; - User user = new User (response, api); + WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userNameChanged"", ""toopher_authentication_enabled"":false}"; + User user = new User (USER_DICT, api); user.RefreshFromServer (); Assert.IsInstanceOf (user); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual (user.id, "1"); Assert.AreEqual (user.name, "userNameChanged"); - Assert.IsTrue (user.toopherAuthenticationEnabled); + Assert.IsFalse (user.toopherAuthenticationEnabled); } [Test] public void UserEnableToopherAuthentication () { var api = getApi(); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":false}"); - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}"; - User user = new User (response, api); + WebClientMock.ReturnValue = USER_RESPONSE; + User user = new User (USER_DICT, api); user.EnableToopherAuthentication (); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["toopher_authentication_enabled"], "true"); @@ -780,9 +783,8 @@ public void UserEnableToopherAuthentication () public void UserDisableToopherAuthentication () { var api = getApi(); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""username"", ""toopher_authentication_enabled"":true}"); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":false}"; - User user = new User (response, api); + User user = new User (USER_DICT, api); user.DisableToopherAuthentication (); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["toopher_authentication_enabled"], "false"); @@ -794,9 +796,8 @@ public void UserDisableToopherAuthentication () public void UserResetTest () { var api = getApi(); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}"); - WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":false}"; - User user = new User (response, api); + WebClientMock.ReturnValue = USER_RESPONSE; + User user = new User (USER_DICT, api); user.Reset (); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["name"], "userName"); @@ -807,13 +808,14 @@ public void UserResetTest () [TestFixture()] public class UserTerminalTests : TestBase { + private IDictionary USER_TERMINAL_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"); + [Test] public void UserTerminalRefreshFromServerTest () { var api = getApi(); - var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalNameChanged"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userNameChanged"", ""toopher_authentication_enabled"":false}}"; - UserTerminal userTerminal = new UserTerminal (response, api); + UserTerminal userTerminal = new UserTerminal (USER_TERMINAL_DICT, api); userTerminal.RefreshFromServer (); Assert.IsInstanceOf (userTerminal); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); From 3292dcf422b5406924401c17144af1fec6e79792 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 16:58:37 -0600 Subject: [PATCH 049/103] Rename getApi to getToopherApi --- ToopherDotNetTests/ToopherDotNetTests.cs | 86 ++++++++++++------------ 1 file changed, 43 insertions(+), 43 deletions(-) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 1fc20c2..b7bb2f0 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -99,7 +99,7 @@ public virtual void Init () OAuthTools.SetDateOverride(null); } - public ToopherApi getApi () + public ToopherApi getToopherApi () { return new ToopherApi ("key", "secret", null, typeof (WebClientMock)); } @@ -301,7 +301,7 @@ public void ToopherBaseUrlTest () [Test] public void CreatePairingWithPhraseTest () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair ("some user", "awkward turtle"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); @@ -313,7 +313,7 @@ public void CreatePairingWithPhraseTest () [Test] public void CreateSmsPairingTest () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair ("some user", "555-555-5555"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); @@ -325,7 +325,7 @@ public void CreateSmsPairingTest () [Test] public void CreateQrPairingTest () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair ("some user"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); @@ -336,7 +336,7 @@ public void CreateQrPairingTest () [Test] public void ArbitraryParametersOnPairTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair ("awkward turtle", "some user", extras: new Dictionary(){{"test_param", "42"}}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); @@ -347,7 +347,7 @@ public void ArbitraryParametersOnPairTest () [Test] public void AuthenticateWithPairingIdTest () { - var api = getApi (); + var api = getToopherApi (); string pairingId = Guid.NewGuid().ToString(); WebClientMock.ReturnValue = @"{""id"":""" + pairingId + @""", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"",""requester_specified_id"":""requesterSpecifiedId"",""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; AuthenticationRequest auth = api.Authenticate(pairingId, "test terminal"); @@ -360,7 +360,7 @@ public void AuthenticateWithPairingIdTest () [Test] public void AuthenticateWithUsernameAndExtrasTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.Authenticate ("some other user", "requester specified id", extras: new Dictionary() {{ "random_key" , "42" }}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); @@ -373,7 +373,7 @@ public void AuthenticateWithUsernameAndExtrasTest () [Test] public void ArbitraryParamtersOnAuthenticateTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.Authenticate ("1", "test terminal", extras: new Dictionary () { { "test_param", "42" } }); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); @@ -384,49 +384,49 @@ public void ArbitraryParamtersOnAuthenticateTest () [Test] public void GenerateAdvancedApiUsageFactory () { - var api = getApi(); + var api = getToopherApi (); Assert.IsInstanceOf (api.advanced); } [Test] public void GenerateAdvancedPairings () { - var api = getApi(); + var api = getToopherApi (); Assert.IsInstanceOf (api.advanced.pairings); } [Test] public void GenerateAdvancedRaw () { - var api = getApi(); + var api = getToopherApi (); Assert.IsInstanceOf (api.advanced.raw); } [Test] public void GenerateAdvancedAuthenticationRequests () { - var api = getApi(); + var api = getToopherApi (); Assert.IsInstanceOf (api.advanced.authenticationRequests); } [Test] public void GenerateAdvancedUsers () { - var api = getApi(); + var api = getToopherApi (); Assert.IsInstanceOf (api.advanced.users); } [Test] public void GenerateAdvancedUserTerminals () { - var api = getApi(); + var api = getToopherApi (); Assert.IsInstanceOf (api.advanced.userTerminals); } [Test] public void AdvancedPairingsGetByIdTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.advanced.pairings.GetById ("1"); Assert.IsInstanceOf (pairing); @@ -440,7 +440,7 @@ public void AdvancedPairingsGetByIdTest () [Test] public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""random_key"":""84""}"; Pairing pairing = api.advanced.pairings.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); @@ -454,7 +454,7 @@ public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest () [Test] public void AdvancedAuthenticationRequestsGetByIdTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.advanced.authenticationRequests.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); @@ -469,7 +469,7 @@ public void AdvancedAuthenticationRequestsGetByIdTest () [Test] public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}, ""random_key"":""84""}"; AuthenticationRequest auth = api.advanced.authenticationRequests.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); @@ -485,7 +485,7 @@ public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest () [Test] public void AdvancedUsersGetByIdTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = USER_RESPONSE; User user = api.advanced.users.GetById ("1"); Assert.IsInstanceOf (user); @@ -498,7 +498,7 @@ public void AdvancedUsersGetByIdTest () [Test] public void AccessArbitraryKeysInAdvancedUsersGetByIdTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true, ""random_key"":""84""}"; User user = api.advanced.users.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); @@ -511,7 +511,7 @@ public void AccessArbitraryKeysInAdvancedUsersGetByIdTest () [Test] public void AdvancedUsersGetByNameTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValueArray = @"[{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}]"; WebClientMock.ReturnValue = USER_RESPONSE; User user = api.advanced.users.GetByName("userName"); @@ -524,7 +524,7 @@ public void AdvancedUsersGetByNameTest () [Test] public void AdvancedUsersCreateTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = USER_RESPONSE; User user = api.advanced.users.Create ("userName"); Assert.IsInstanceOf (user); @@ -537,7 +537,7 @@ public void AdvancedUsersCreateTest () [Test] public void AdvancedUsersCreateWithParamsTest () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = USER_RESPONSE; NameValueCollection parameters = new NameValueCollection(); parameters.Add ("foo", "bar"); @@ -553,7 +553,7 @@ public void AdvancedUsersCreateWithParamsTest () [Test] public void AdvancedUserTerminalsGetByIdTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; UserTerminal userTerminal = api.advanced.userTerminals.GetById ("1"); Assert.IsInstanceOf (userTerminal); @@ -567,7 +567,7 @@ public void AdvancedUserTerminalsGetByIdTest () [Test] public void AccessArbitraryKeysInAdvancedUserTerminalsGetByIdTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"",""random_key"":""84"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; UserTerminal userTerminal = api.advanced.userTerminals.GetById ("1"); Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); @@ -580,7 +580,7 @@ public void AccessArbitraryKeysInAdvancedUserTerminalsGetByIdTest () [Test] public void AdvancedUserTerminalsCreateTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; UserTerminal userTerminal = api.advanced.userTerminals.Create ("userName", "userTerminalName", "requesterSpecifiedId"); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); @@ -593,7 +593,7 @@ public void AdvancedUserTerminalsCreateTest () [Test] public void AdvancedUserTerminalsCreateWithParamsTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; NameValueCollection parameters = new NameValueCollection(); parameters.Add ("foo", "bar"); @@ -610,7 +610,7 @@ public void AdvancedUserTerminalsCreateWithParamsTest () [ExpectedException(typeof(UserDisabledError))] public void DisabledUserRaisesCorrectErrorTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":704, ""error_message"":""The specified user has disabled Toopher authentication.""}"); api.Authenticate ("some disabled user", "some random string"); @@ -620,7 +620,7 @@ public void DisabledUserRaisesCorrectErrorTest () [ExpectedException (typeof (UserUnknownError))] public void UnknownUserRaisesCorrectErrorTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":705, ""error_message"":""No matching user exists.""}"); api.Authenticate ("some unknown user", "some random string"); @@ -630,7 +630,7 @@ public void UnknownUserRaisesCorrectErrorTest () [ExpectedException (typeof (TerminalUnknownError))] public void UnknownTerminalRaisesCorrectErrorTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":706, ""error_message"":""No matching terminal exists.""}"); api.Authenticate ("some unknown user", "some random string"); @@ -640,7 +640,7 @@ public void UnknownTerminalRaisesCorrectErrorTest () [ExpectedException (typeof (PairingDeactivatedError))] public void DeactivatedPairingRaisesCorrectErrorTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":601, ""error_message"":""This pairing has been deactivated.""}"); api.Authenticate ("some disabled user", "some random string"); @@ -650,7 +650,7 @@ public void DeactivatedPairingRaisesCorrectErrorTest () [ExpectedException (typeof (PairingDeactivatedError))] public void UnauthorizedPairingRaisesCorrectErrorTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":601, ""error_message"":""This pairing has not been authorized to authenticate.""}"); api.Authenticate ("some unauthorized user", "some random string"); @@ -665,7 +665,7 @@ public class PairingTests : TestBase [Test] public void PairingRefreshFromServerTest () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""enabled"":false, ""user"":{""id"":""1"",""name"":""userNameChanged"", ""toopher_authentication_enabled"":true}}"; Pairing pairing = new Pairing (PAIRING_DICT, api); pairing.RefreshFromServer (); @@ -680,7 +680,7 @@ public void PairingRefreshFromServerTest () [Test] public void PairingGetResetLinkTest () { - var api = getApi(); + var api = getToopherApi (); string link = "http://api.toopher.test/v1/pairings/1/reset?reset_authorization=abcde"; WebClientMock.ReturnValue = @"{""url"":""" + link + @"""}"; Pairing pairing = new Pairing (PAIRING_DICT, api); @@ -692,7 +692,7 @@ public void PairingGetResetLinkTest () [Test] public void PairingEmailResetLinkTest () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = @"{}"; Pairing pairing = new Pairing (PAIRING_DICT, api); pairing.EmailResetLink ("test@test.com"); @@ -703,7 +703,7 @@ public void PairingEmailResetLinkTest () [Test] public void PairingGetQrCodeImage () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = @"{}"; Pairing pairing = new Pairing (PAIRING_DICT, api); pairing.GetQrCodeImage (); @@ -720,7 +720,7 @@ public class AuthenticationRequestTests : TestBase [Test] public void AuthenticationRequestRefreshFromServerTest () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""granted"":true, ""automated"":false, ""reason_code"":2, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal CHANGED"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user CHANGED"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName CHANGED""}}"; AuthenticationRequest auth = new AuthenticationRequest (AUTH_REQUEST_DICT, api); auth.RefreshFromServer (); @@ -736,7 +736,7 @@ public void AuthenticationRequestRefreshFromServerTest () [Test] public void AuthenticationRequestGrantWithOtpTest () { - var api = getApi (); + var api = getToopherApi (); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = new AuthenticationRequest (AUTH_REQUEST_DICT, api); auth.GrantWithOtp ("123456"); @@ -755,7 +755,7 @@ public class UserTests : TestBase [Test] public void UserRefreshFromServerTest () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userNameChanged"", ""toopher_authentication_enabled"":false}"; User user = new User (USER_DICT, api); user.RefreshFromServer (); @@ -769,7 +769,7 @@ public void UserRefreshFromServerTest () [Test] public void UserEnableToopherAuthentication () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = USER_RESPONSE; User user = new User (USER_DICT, api); user.EnableToopherAuthentication (); @@ -782,7 +782,7 @@ public void UserEnableToopherAuthentication () [Test] public void UserDisableToopherAuthentication () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":false}"; User user = new User (USER_DICT, api); user.DisableToopherAuthentication (); @@ -795,7 +795,7 @@ public void UserDisableToopherAuthentication () [Test] public void UserResetTest () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = USER_RESPONSE; User user = new User (USER_DICT, api); user.Reset (); @@ -813,7 +813,7 @@ public class UserTerminalTests : TestBase [Test] public void UserTerminalRefreshFromServerTest () { - var api = getApi(); + var api = getToopherApi (); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalNameChanged"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userNameChanged"", ""toopher_authentication_enabled"":false}}"; UserTerminal userTerminal = new UserTerminal (USER_TERMINAL_DICT, api); userTerminal.RefreshFromServer (); From 0826017aabf0448edb1b9bcc962550ad265ae422 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 16:59:00 -0600 Subject: [PATCH 050/103] Add tests for ToopherIframe version and baseUrl --- ToopherDotNetTests/ToopherDotNetTests.cs | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index b7bb2f0..9fcbeec 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -125,6 +125,19 @@ private ToopherIframe getToopherIframeApi () return new ToopherIframe (TOOPHER_CONSUMER_KEY, TOOPHER_CONSUMER_SECRET, DEFAULT_BASE_URL, typeof (WebClientMock)); } + [Test] + public void ToopherIframeVersionTest () + { + Assert.IsTrue (int.Parse(ToopherIframe.IFRAME_VERSION) >= 1); + } + + [Test] + public void ToopherBaseUrlTest () + { + StringAssert.Contains ("https", ToopherIframe.DEFAULT_BASE_URL); + Assert.IsTrue (System.Uri.IsWellFormedUriString (ToopherIframe.DEFAULT_BASE_URL, System.UriKind.Absolute)); + } + [Test] public void GetAuthenticationUrlTest () { From 4ce9e4c76c073a081cdd9d13e57ee0fe7b91d797 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 17:03:49 -0600 Subject: [PATCH 051/103] Add test for create User, UserTerminal, Action, Pairing and AuthenticationRequest --- ToopherDotNetTests/ToopherDotNetTests.cs | 71 ++++++++++++++++++++++++ 1 file changed, 71 insertions(+) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 9fcbeec..034178e 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -675,6 +675,19 @@ public class PairingTests : TestBase { private IDictionary PAIRING_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); + [Test] + public void CreatePairingTest () + { + var api = getToopherApi (); + Pairing pairing = new Pairing (PAIRING_DICT, api); + Assert.AreEqual (pairing.id, "1"); + Assert.IsFalse (pairing.pending); + Assert.IsTrue (pairing.enabled); + Assert.AreEqual (pairing.user.id, "1"); + Assert.AreEqual (pairing.user.name, "userName"); + Assert.IsTrue (pairing.user.toopherAuthenticationEnabled); + } + [Test] public void PairingRefreshFromServerTest () { @@ -730,6 +743,27 @@ public class AuthenticationRequestTests : TestBase { private IDictionary AUTH_REQUEST_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":true, ""granted"":false, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"); + [Test] + public void CreateAuthenticationRequestTest () + { + var api = getToopherApi (); + AuthenticationRequest auth = new AuthenticationRequest (AUTH_REQUEST_DICT, api); + Assert.AreEqual (auth.id, "1"); + Assert.IsTrue (auth.pending); + Assert.IsFalse (auth.granted); + Assert.IsFalse (auth.automated); + Assert.AreEqual (auth.reasonCode, 1); + Assert.AreEqual (auth.reason, "its a test"); + Assert.AreEqual (auth.terminal.id, "1"); + Assert.AreEqual (auth.terminal.name, "test terminal"); + Assert.AreEqual (auth.terminal.requesterSpecifiedId, "requesterSpecifiedId"); + Assert.AreEqual (auth.user.id, "1"); + Assert.AreEqual (auth.user.name, "some user"); + Assert.IsTrue (auth.user.toopherAuthenticationEnabled); + Assert.AreEqual (auth.action.id, "1"); + Assert.AreEqual (auth.action.name, "actionName"); + } + [Test] public void AuthenticationRequestRefreshFromServerTest () { @@ -760,11 +794,22 @@ public void AuthenticationRequestGrantWithOtpTest () } } + [TestFixture()] public class UserTests : TestBase { private IDictionary USER_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}"); + [Test] + public void CreateUserTest () + { + var api = getToopherApi (); + Toopher.User user = new Toopher.User (USER_DICT, api); + Assert.AreEqual (user.id, "1"); + Assert.AreEqual (user.name, "userName"); + Assert.IsTrue (user.toopherAuthenticationEnabled); + } + [Test] public void UserRefreshFromServerTest () { @@ -823,6 +868,19 @@ public class UserTerminalTests : TestBase { private IDictionary USER_TERMINAL_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"); + [Test] + public void CreateUserTerminalTest () + { + var api = getToopherApi (); + UserTerminal userTerminal = new UserTerminal (USER_TERMINAL_DICT, api); + Assert.AreEqual (userTerminal.id, "1"); + Assert.AreEqual (userTerminal.name, "userTerminalName"); + Assert.AreEqual (userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); + Assert.AreEqual (userTerminal.user.id, "1"); + Assert.AreEqual (userTerminal.user.name, "userName"); + Assert.IsTrue (userTerminal.user.toopherAuthenticationEnabled); + } + [Test] public void UserTerminalRefreshFromServerTest () { @@ -838,4 +896,17 @@ public void UserTerminalRefreshFromServerTest () Assert.IsFalse (userTerminal.user.toopherAuthenticationEnabled); } } + + [TestFixture()] + public class ActionTests : TestBase + { + [Test] + public void CreateActionTest () + { + var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""actionName""}"); + Toopher.Action action = new Toopher.Action (response); + Assert.AreEqual (action.id, "1"); + Assert.AreEqual (action.name, "actionName"); + } + } } From 38ce5f7c1e7eb8a1da3f04d13ee84104010c153c Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 17:07:34 -0600 Subject: [PATCH 052/103] Update ToopherIframe constants to allow for testing --- ToopherDotNet/ToopherDotNet.cs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index d6c1b19..dfda033 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -16,9 +16,9 @@ namespace Toopher { public class ToopherIframe { - private const string IFRAME_VERSION = "2"; - private const long DEFAULT_TTL = 300L; - private const string DEFAULT_BASE_URL = "https://api.toopher.com/v1/"; + public const string IFRAME_VERSION = "2"; + public const long DEFAULT_TTL = 300L; + public const string DEFAULT_BASE_URL = "https://api.toopher.com/v1/"; private string baseUrl; private string consumerKey; From c6994a38623e10e7cea4c70a863692b2c91dd986 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 17 Feb 2015 17:09:25 -0600 Subject: [PATCH 053/103] Reorder ToopherIframe methods --- ToopherDotNet/ToopherDotNet.cs | 34 +++++++++++++++++----------------- 1 file changed, 17 insertions(+), 17 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index dfda033..c67eb59 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -36,12 +36,6 @@ private static DateTime GetDate () return dateOverride ?? DateTime.UtcNow; } - private static int GetUnixEpochTimeInSeconds () - { - TimeSpan t = (GetDate() - new DateTime(1970, 1, 1)); - return (int) t.TotalSeconds; - } - public ToopherIframe (string consumerKey, string consumerSecret, string baseUrl = null, Type webClientProxyType = null) { this.consumerKey = consumerKey; @@ -165,6 +159,22 @@ public Dictionary ValidatePostback (Dictionary } } + private string GetOauthUrl (string url, NameValueCollection parameters) + { + OAuthRequest client = OAuthRequest.ForRequestToken (consumerKey, consumerSecret); + client.RequestUrl = url; + + string oauthParams = client.GetAuthorizationQuery (parameters); + string requestParams = UrlEncodeParameters (parameters); + return url + "?" + requestParams + "&" + oauthParams; + } + + private static int GetUnixEpochTimeInSeconds () + { + TimeSpan t = (GetDate() - new DateTime(1970, 1, 1)); + return (int) t.TotalSeconds; + } + private static string Signature (string secret, string maybeSignature, Dictionary data) { Dictionary sortedData = data.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); @@ -180,17 +190,7 @@ private static string Signature (string secret, string maybeSignature, Dictionar } } - private string GetOauthUrl (string url, NameValueCollection parameters) - { - OAuthRequest client = OAuthRequest.ForRequestToken (consumerKey, consumerSecret); - client.RequestUrl = url; - - string oauthParams = client.GetAuthorizationQuery (parameters); - string requestParams = UrlEncodeParameters (parameters); - return url + "?" + requestParams + "&" + oauthParams; - } - - private string UrlEncodeParameters (NameValueCollection parameters) + private static string UrlEncodeParameters (NameValueCollection parameters) { WebParameterCollection collection = new WebParameterCollection(parameters); foreach (var parameter in collection) From 088e4ef902c435adc969625ef9d4fbb2db2f03ea Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 09:00:01 -0600 Subject: [PATCH 054/103] Remove webClientProxyType from ToopherIframe and fix DEFAULT_BASE_URL --- ToopherDotNet/ToopherDotNet.cs | 8 +------- ToopherDotNetTests/ToopherDotNetTests.cs | 2 +- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index c67eb59..eb1b48a 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -23,7 +23,6 @@ public class ToopherIframe private string baseUrl; private string consumerKey; private string consumerSecret; - private Type webClientProxyType; private static DateTime? dateOverride; public static void SetDateOverride (DateTime? dateOverride) @@ -43,12 +42,7 @@ public ToopherIframe (string consumerKey, string consumerSecret, string baseUrl if (baseUrl != null) { this.baseUrl = baseUrl; } else { - this.baseUrl = ToopherApi.DEFAULT_BASE_URL; - } - if (webClientProxyType != null) { - this.webClientProxyType = webClientProxyType; - } else { - this.webClientProxyType = typeof(WebClientProxy); + this.baseUrl = DEFAULT_BASE_URL; } } diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 034178e..c8ae3a9 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -122,7 +122,7 @@ public override void Init () private ToopherIframe getToopherIframeApi () { - return new ToopherIframe (TOOPHER_CONSUMER_KEY, TOOPHER_CONSUMER_SECRET, DEFAULT_BASE_URL, typeof (WebClientMock)); + return new ToopherIframe (TOOPHER_CONSUMER_KEY, TOOPHER_CONSUMER_SECRET, DEFAULT_BASE_URL); } [Test] From 21f0e3844971aa691034f6b10510fa7460b465a8 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 09:39:52 -0600 Subject: [PATCH 055/103] Add XML doc comments for ToopherIframe --- ToopherDotNet/ToopherDotNet.cs | 40 +++++++++++++++++++++++++++++++--- 1 file changed, 37 insertions(+), 3 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index eb1b48a..97a7cf9 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -14,6 +14,10 @@ namespace Toopher { + /// + /// DotNet helper library to generate Toopher iFrame requests and validate responses. + /// Register at https://dev.toopher.com to get your Toopher Developer API Credentials. + /// public class ToopherIframe { public const string IFRAME_VERSION = "2"; @@ -35,7 +39,13 @@ private static DateTime GetDate () return dateOverride ?? DateTime.UtcNow; } - public ToopherIframe (string consumerKey, string consumerSecret, string baseUrl = null, Type webClientProxyType = null) + /// + /// Create an instance of the ToopherIframe helper for the specified API URL. + /// + /// Your Toopher API OAuth Consumer Key. + /// Your Toopher API OAuth Consumer Secret. + /// The base URL of the Toopher API to target. If blank, the default is "https://api.toopher.com/v1/". + public ToopherIframe (string consumerKey, string consumerSecret, string baseUrl = null) { this.consumerKey = consumerKey; this.consumerSecret = consumerSecret; @@ -46,6 +56,16 @@ public ToopherIframe (string consumerKey, string consumerSecret, string baseUrl } } + /// + /// Generate a URL to retrieve a Toopher Authentication iFrame for a given user. + /// + /// Unique name that identifies this user. This will be displayed to the user on their mobile device when they pair or authenticate. + /// Email adddress that the user has access to. In case the user has lost or cannot access their mobile device, Toopher will send a reset email to this address. + /// Optional, can be empty. Toopher will include this token in the signed data returned with the iFrame response. + /// The name of the action to authenticate; will be shown to the user. If blank, the Toopher API will default the action to "Log In". + /// Optional, can be empty. Toopher will include this value in the signed data returned with the iFrame response. + /// An optional Dictionary of extra parameters to provide to the API. + /// A string URL that can be used to retrieve the Authentication iFrame by the user's browser. public string GetAuthenticationUrl (string userName, string resetEmail, string requestToken, string actionName = "Log In", string requesterMetadata = "None", Dictionary extras = null) { NameValueCollection parameters = new NameValueCollection (); @@ -73,6 +93,13 @@ public string GetAuthenticationUrl (string userName, string resetEmail, string r return GetOauthUrl (baseUrl + "web/authenticate", parameters); } + /// + /// Generate a URL to retrieve a Toopher Pairing iFrame for a given user. + /// + /// Unique name that identifies this user. This will be displayed to the user on their mobile device when they pair or authenticate. + /// Email address that the user has access to. In case the user has lost or cannot access their mobile device, Toopher will send a reset email to this address. + /// An optional Dictionary of extra parameters to provide to the API. + /// A string URL that can be used to retrieve the Pairing iFrame by the user's browser. public string GetUserManagementUrl (string userName, string resetEmail, Dictionary extras = null) { NameValueCollection parameters = new NameValueCollection (); @@ -97,13 +124,20 @@ public string GetUserManagementUrl (string userName, string resetEmail, Dictiona return GetOauthUrl (baseUrl + "web/manage_user", parameters); } - public Dictionary ValidatePostback (Dictionary extras, string sessionToken, long ttl) + /// + /// Verify the authenticity of data returned from the Toopher iFrame by validating the crytographic signature. + /// + /// The data returned from the iFrame. + /// The session token + /// Time-To-Live (seconds) to enforce on the Toopher API signature. This value sets the maximum duration between the Toopher API creating the signature and the signature being validated on your server. + /// A Dictionary of the validated data if the signature is valid, or null if the signature is invalid. + public Dictionary ValidatePostback (Dictionary parameters, string sessionToken, long ttl) { try { List missingKeys = new List (); Dictionary data = new Dictionary(); - foreach (var entry in extras) + foreach (var entry in parameters) { if (entry.Value.Length > 0) { data.Add(entry.Key, entry.Value[0]); From 549e4195066e67295f1f4a4412b76f5e3ab9b676 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 13:02:13 -0600 Subject: [PATCH 056/103] Add XML doc comments for ToopherApi --- ToopherDotNet/ToopherDotNet.cs | 92 +++++++++++++++++++++++++--------- 1 file changed, 68 insertions(+), 24 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 97a7cf9..512ee7c 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -243,22 +243,18 @@ public class ToopherApi public const string DEFAULT_BASE_URL = "https://api.toopher.com/v1/"; public ToopherApi.AdvancedApiUsageFactory advanced; - string consumerKey; - string consumerSecret; - string baseUrl; - Type webClientProxyType; - - // Create the ToopherApi object tied to your requester credentials - // - // Credentials are available on https://dev.toopher.com + private string consumerKey; + private string consumerSecret; + private string baseUrl; + private Type webClientProxyType; + /// - /// Create a new instance of the ToopherApi client tied to your requester - /// credentials. Credentials are available at https://dev.toopher.com + /// Create an instance of the ToopherAPI helper for the specified API URL. /// - /// OAuth Consumer Key - /// OAuth Consumer Secret - /// Override url for ToopherApi webservice (default=https://api.toopher.com/v1/) - /// Override WebClient class for testing purposes + /// Your Toopher API OAuth Consumer Key + /// Your Toopher API OAuth Consumer Secret + /// The base URL of the Toopher API to target. If blank, the default is "https://api.toopher.com/v1/". + /// The type of web client. Override WebClient class for testing. If blank, the default is WebClientProxy. public ToopherApi (string consumerKey, string consumerSecret, string baseUrl = null, Type webClientProxyType = null) { this.advanced = new ToopherApi.AdvancedApiUsageFactory(this); @@ -276,11 +272,16 @@ public ToopherApi (string consumerKey, string consumerSecret, string baseUrl = n } } - // Create an SMS pairing, QR pairing or regular pairing - // - // Must provide a username and phone number for an SMS pairing - // Must provide a username for a QR pairing - // Must provide a username and pairing phrase for a regular pairing + /// + /// Create an SMS pairing, QR pairing or regular pairing. + /// Must provide a username and phone number for an SMS pairing. + /// Must provide a username for a QR pairing. + /// Must provide a username and pairing phrase for a regular pairing. + /// + /// A user-facing descriptive name for the user (displayed in requests). + /// The pairing phrase or phone number supplied by the user. + /// An optional Dictionary of extra parameters to provide to the API. + /// A object. public Pairing Pair (string userName, string pairingPhraseOrNum = null, Dictionary extras = null) { string endpoint; @@ -310,11 +311,14 @@ public Pairing Pair (string userName, string pairingPhraseOrNum = null, Dictiona return new Pairing (json, this); } - // Initiate an authentication request by pairing id or username - // - // Provide pairing ID or username, a name for the terminal (displayed to user) or requester-specified ID, - // an optional action name (displayed to user) [defaults to "log in"] and - // an optional Dictionary of extras to be sent to the API + /// + /// Initiate an authentication request by pairing id or username + /// + /// The pairing id or username indicating whom the request should be sent to. + /// The user-facing descriptive name for the terminal from which the request originates or the unique identifier for this terminal. Not displayed to the user. + /// The user-facing descriptive name for the action which is being authenticated. + /// An optional Dictionary of extra parameters to provide to the API. + /// An object. public AuthenticationRequest Authenticate (string pairingIdOrUsername, string terminalNameOrTerminalNameExtra, string actionName = null, Dictionary extras = null) { string endpoint = "authentication_requests/initiate"; @@ -369,6 +373,11 @@ public Pairings (ToopherApi toopherApi) this.api = toopherApi; } + /// + /// Retrieve the current status of a pairing. + /// + /// The unique id for a pairing. + /// A object. public Pairing GetById (string pairingId) { string endpoint = string.Format ("pairings/{0}", pairingId); @@ -386,6 +395,11 @@ public AuthenticationRequests (ToopherApi toopherApi) this.api = toopherApi; } + /// + /// Retrieve the current status of an authenticationrequest. + /// + /// The unique id for an authentication request. + /// A object. public AuthenticationRequest GetById (string authenticationRequestId) { string endpoint = string.Format ("authentication_requests/{0}", authenticationRequestId); @@ -403,6 +417,11 @@ public Users (ToopherApi toopherApi) this.api = toopherApi; } + /// + /// Retrieve the current status of a user with the user id. + /// + /// The unique id for a user. + /// A object. public User GetById (string userId) { string endpoint = string.Format ("users/{0}", userId); @@ -410,6 +429,12 @@ public User GetById (string userId) return new User (json, api); } + + /// + /// Retrieve the current status of a user with the user name. + /// + /// The name of the user. + /// A object. public User GetByName (string userName) { string endpoint = "users"; @@ -428,6 +453,12 @@ public User GetByName (string userName) return GetById (userId); } + /// + /// Create a new user with a user name. + /// + /// The name of the user. + /// An optional collection of parameters to provide to the API. + /// A object. public User Create (string userName, NameValueCollection parameters = null) { string endpoint = "users/create"; @@ -449,6 +480,11 @@ public UserTerminals (ToopherApi toopherApi) this.api = toopherApi; } + /// + /// Retrieve the current status of a user terminal. + /// + /// The unique id for a user terminal. + /// A object. public UserTerminal GetById (string userTerminalId) { string endpoint = string.Format ("user_terminals/{0}", userTerminalId); @@ -456,6 +492,14 @@ public UserTerminal GetById (string userTerminalId) return new UserTerminal (json, api); } + /// + /// Create a new user terminal + /// + /// The name of the user. + /// The user-facing descriptive name for the terminal from which the request originates + /// The requester specified id that uniquely idenfities this terminal. + /// An optional collection of parameters to provide to the API. + /// A object. public UserTerminal Create (string userName, string terminalName, string requesterSpecifiedId, NameValueCollection parameters = null) { string endpoint = "user_terminals/create"; From 0d9803ece421c1b634118c8d275f13295553aadd Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 13:07:17 -0600 Subject: [PATCH 057/103] Add XML doc comments for Pairing --- ToopherDotNet/ToopherDotNet.cs | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 512ee7c..d8cb5dd 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -639,7 +639,6 @@ private void parseRequestError (JsonObject err) } - // Status information for a pairing request public class Pairing { private IDictionary rawResponse; @@ -675,6 +674,11 @@ public override string ToString () return string.Format ("[Pairing: id={0}; enabled={1}; pending={2}; userId={3}; userName={4}; userToopherAuthenticationEnabled={5}]", id, enabled, pending, user.id, user.name, user.toopherAuthenticationEnabled); } + /// + /// Provide information about the status of a pairing. + /// + /// The response from the API. + /// The Toopher API associated with this pairing. public Pairing (IDictionary response, ToopherApi toopherApi) { this.rawResponse = response; @@ -689,6 +693,9 @@ public Pairing (IDictionary response, ToopherApi toopherApi) } } + /// + /// Update the pairing object with response from the API. + /// public void RefreshFromServer () { string endpoint = string.Format ("pairings/{0}", id); @@ -696,6 +703,11 @@ public void RefreshFromServer () Update (json); } + /// + /// Retrieve link to allow user to reset the pairing. + /// + /// An optional Dictionary of extra parameters to provide to the API. + /// A reset link as a string public string GetResetLink (Dictionary extras = null) { string endpoint = string.Format ("pairings/{0}/generate_reset_link", id); @@ -709,6 +721,11 @@ public string GetResetLink (Dictionary extras = null) return (string)json["url"]; } + /// + /// Send reset link to user via email. + /// + /// The email address where the reset link is sent. + /// An optional Dictionary of extra parameters to provide to the API. public void EmailResetLink (string email, Dictionary extras = null) { string endpoint = string.Format ("pairings/{0}/send_reset_link", id); @@ -722,6 +739,10 @@ public void EmailResetLink (string email, Dictionary extras = nu api.advanced.raw.post(endpoint, parameters); } + /// + /// Retrieve QR code image for the pairing. + /// + /// QR code image stored as a byte[]. public byte[] GetQrCodeImage () { string endpoint = string.Format ("qr/pairings/{0}", id); From 0e4f03ce8a657eacbd21bcec53b4fd400bed6188 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 13:12:07 -0600 Subject: [PATCH 058/103] Add XML doc comments for AuthenticationRequest --- ToopherDotNet/ToopherDotNet.cs | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index d8cb5dd..a942f3d 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -763,7 +763,6 @@ private void Update (IDictionary response) } } - // Status information for an authentication request public class AuthenticationRequest { private IDictionary rawResponse; @@ -816,12 +815,16 @@ public override string ToString () return string.Format ("[AuthenticationRequest: id={0}; pending={1}; granted={2}; automated={3}; reasonCode={4}; reason={5}; actionId={6}; actionName={7}; terminalId={8}; terminalName={9}; terminalRequesterSpecifiedId={10}; userId={11}; userName={12}; userToopherAuthenticationEnabled={13}]", id, pending, granted, automated, reasonCode, reason, action.id, action.name, terminal.id, terminal.name, terminal.requesterSpecifiedId, user.id, user.name, user.toopherAuthenticationEnabled); } + /// + /// Provide information about the status of an authentication request. + /// + /// The response from the API. + /// The Toopher API associated with this authentication request. public AuthenticationRequest (IDictionary response, ToopherApi toopherApi) { this.rawResponse = response; this.api = toopherApi; try { - // validate that the json has the minimum keys we need this.id = (string)response["id"]; this.pending = (bool)response["pending"]; this.granted = (bool)response["granted"]; @@ -836,6 +839,9 @@ public AuthenticationRequest (IDictionary response, ToopherApi t } } + /// + /// Update the authentication request object with response from the API. + /// public void RefreshFromServer () { string endpoint = string.Format ("authentication_requests/{0}", id); @@ -843,6 +849,10 @@ public void RefreshFromServer () Update (json); } + /// + /// Grant the authentication request with an OTP. + /// + /// One-time password for the authentication request. public void GrantWithOtp (string otp) { string endpoint = string.Format ("authentication_requests/{0}/otp_auth", id); From d2a3d5db9fb3d4b5da3016e1013680ac1f89881c Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 13:17:00 -0600 Subject: [PATCH 059/103] Add XML doc comments for User --- ToopherDotNet/ToopherDotNet.cs | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index a942f3d..685c3c4 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -914,6 +914,11 @@ public override string ToString () return string.Format ("[User: id={0}; name={1}; toopherAuthenticationEnabled={2}]", id, name, toopherAuthenticationEnabled); } + /// + /// Provide information about the status of a user. + /// + /// The response from the API. + /// The Toopher API associated with this authentication request. public User (IDictionary response, ToopherApi toopherApi) { this.rawResponse = response; @@ -927,6 +932,9 @@ public User (IDictionary response, ToopherApi toopherApi) } } + /// + /// Update the user object with response from the API. + /// public void RefreshFromServer () { string endpoint = string.Format ("users/{0}", id); @@ -934,6 +942,10 @@ public void RefreshFromServer () Update (json); } + /// + /// Update the user object with provided response. + /// + /// The response from the API. public void Update (IDictionary response) { this.rawResponse = response; @@ -945,6 +957,9 @@ public void Update (IDictionary response) } } + /// + /// Enable Toopher Authentication for an individual user. + /// public void EnableToopherAuthentication () { string endpoint = string.Format ("users/{0}", id); @@ -954,6 +969,11 @@ public void EnableToopherAuthentication () Update (json); } + /// + /// Disable Toopher Authentication for an individual user. If the user is + /// disabled, future attempts to authenticate the user with Toopher will return + /// a UserDisabledError. + /// public void DisableToopherAuthentication () { string endpoint = string.Format ("users/{0}", id); @@ -963,6 +983,9 @@ public void DisableToopherAuthentication () Update (json); } + /// + /// Remove all pairings for the user. + /// public void Reset () { string endpoint = "users/reset"; From f059429bf88d50009a06899b3fe7659afe13a297 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 13:19:55 -0600 Subject: [PATCH 060/103] Add XML doc comments for UserTerminal --- ToopherDotNet/ToopherDotNet.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 685c3c4..94afccf 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -1030,6 +1030,11 @@ public override string ToString () return string.Format ("[UserTerminal: id={0}; name={1}; requesterSpecifiedId={2}; userId={3}, userName={4}, userToopherAuthenticationEnabled={5}]", id, name, requesterSpecifiedId, user.id, user.name, user.toopherAuthenticationEnabled); } + /// + /// Provide information about the status of a user terminal. + /// + /// The response from the API. + /// The Toopher API associated with this authentication request. public UserTerminal (IDictionary response, ToopherApi toopherApi) { this.rawResponse = response; @@ -1044,6 +1049,9 @@ public UserTerminal (IDictionary response, ToopherApi toopherApi } } + /// + /// Update the user terminal object with response from the API. + /// public void RefreshFromServer () { string endpoint = string.Format ("user_terminals/{0}", id); @@ -1051,6 +1059,10 @@ public void RefreshFromServer () Update (json); } + /// + /// Update the user object with provided response. + /// + /// The response from the API. public void Update (IDictionary response) { this.rawResponse = response; From 221d6b8d02c6c07e0b40337bda41826efab6cdcc Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 13:20:14 -0600 Subject: [PATCH 061/103] Add XML doc comments for Action --- ToopherDotNet/ToopherDotNet.cs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 94afccf..c1e0723 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -1103,6 +1103,10 @@ public override string ToString () return string.Format ("[Action: id={0}; name={1}]", id, name); } + /// + /// Provide information about the status of an action. + /// + /// The response from the API. public Action (IDictionary response) { this.rawResponse = response; @@ -1114,6 +1118,10 @@ public Action (IDictionary response) } } + /// + /// Update the user object with provided response. + /// + /// The response from the API. public void Update (IDictionary response) { this.rawResponse = response; From 78d88db8afdb210c47d464f01ae032044c76b80a Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 13:26:13 -0600 Subject: [PATCH 062/103] Throw SignatureValidationError if there's an error while calculating signature --- ToopherDotNet/ToopherDotNet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index c1e0723..598d1cf 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -169,7 +169,7 @@ public Dictionary ValidatePostback (Dictionary var computedSig = Signature(consumerSecret, maybeSignature, data); signatureValid = computedSig == maybeSignature; } catch (Exception e) { - signatureValid = false; + throw new SignatureValidationError ("Error while calculating signature: " + e); } if (!signatureValid) { From 2c8f1e5a6e1152bc3de548d5f4858162e91875f0 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 14:40:32 -0600 Subject: [PATCH 063/103] Rename terminal_name_extra to requester_specified_terminal_id --- ToopherDotNet/ToopherDotNet.cs | 9 +++++---- ToopherDotNetTests/ToopherDotNetTests.cs | 2 +- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 598d1cf..60bcbeb 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -313,13 +313,14 @@ public Pairing Pair (string userName, string pairingPhraseOrNum = null, Dictiona /// /// Initiate an authentication request by pairing id or username + /// Must provide a pairing id and terminal name or username and requester specified id /// /// The pairing id or username indicating whom the request should be sent to. - /// The user-facing descriptive name for the terminal from which the request originates or the unique identifier for this terminal. Not displayed to the user. + /// The user-facing descriptive name for the terminal from which the request originates or the unique identifier for this terminal. Not displayed to the user. /// The user-facing descriptive name for the action which is being authenticated. /// An optional Dictionary of extra parameters to provide to the API. /// An object. - public AuthenticationRequest Authenticate (string pairingIdOrUsername, string terminalNameOrTerminalNameExtra, string actionName = null, Dictionary extras = null) + public AuthenticationRequest Authenticate (string pairingIdOrUsername, string terminalNameOrRequesterSpecifiedId, string actionName = null, Dictionary extras = null) { string endpoint = "authentication_requests/initiate"; NameValueCollection parameters = new NameValueCollection (); @@ -327,10 +328,10 @@ public AuthenticationRequest Authenticate (string pairingIdOrUsername, string te try { new Guid(pairingIdOrUsername); parameters.Add ("pairing_id", pairingIdOrUsername); - parameters.Add ("terminal_name", terminalNameOrTerminalNameExtra); + parameters.Add ("terminal_name", terminalNameOrRequesterSpecifiedId); } catch (Exception) { parameters.Add ("user_name", pairingIdOrUsername); - parameters.Add ("terminal_name_extra", terminalNameOrTerminalNameExtra); + parameters.Add ("requester_specified_terminal_id", terminalNameOrRequesterSpecifiedId); } if (actionName != null && actionName.Length > 0) { diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index c8ae3a9..1b803d7 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -378,7 +378,7 @@ public void AuthenticateWithUsernameAndExtrasTest () AuthenticationRequest auth = api.Authenticate ("some other user", "requester specified id", extras: new Dictionary() {{ "random_key" , "42" }}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some other user"); - Assert.AreEqual (WebClientMock.LastRequestData["terminal_name_extra"], "requester specified id"); + Assert.AreEqual (WebClientMock.LastRequestData["requester_specified_terminal_id"], "requester specified id"); Assert.AreEqual (WebClientMock.LastRequestData["random_key"], "42"); Assert.AreEqual (auth.id, "1"); } From f67e25c838a6f9d5bd92a6d4e56bea1e46b57ad2 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 14:45:11 -0600 Subject: [PATCH 064/103] Fix spacing --- ToopherDotNet/ToopherDotNet.cs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 60bcbeb..05e71c7 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -1180,7 +1180,7 @@ public class PairingDeactivatedError : RequestError /// /// Design-For-Testability shims from here on down... /// - + public class WebClientProxy : IDisposable { WebClient _theClient = new WebClient (); @@ -1255,4 +1255,3 @@ public string StatusDescription } } - From fa30ccec71192bf0238e72c84770fc9744c10e12 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 15:45:45 -0600 Subject: [PATCH 065/103] Update ToopherDotNetDemo --- ToopherDotNetDemo/ToopherDotNetDemo.cs | 44 ++++++++++++++------------ 1 file changed, 24 insertions(+), 20 deletions(-) diff --git a/ToopherDotNetDemo/ToopherDotNetDemo.cs b/ToopherDotNetDemo/ToopherDotNetDemo.cs index 3960d81..a2ac8ba 100644 --- a/ToopherDotNetDemo/ToopherDotNetDemo.cs +++ b/ToopherDotNetDemo/ToopherDotNetDemo.cs @@ -21,17 +21,20 @@ public static void Main (string[] args) string consumerKey = System.Environment.GetEnvironmentVariable ("TOOPHER_CONSUMER_KEY"); string consumerSecret = System.Environment.GetEnvironmentVariable ("TOOPHER_CONSUMER_SECRET"); if ((consumerKey == null) || (consumerSecret == null)) { + Console.WriteLine ("Setup Credentials"); + Console.WriteLine ("--------------------------------------"); Console.WriteLine ("Enter your requester credentials (from https://dev.toopher.com)."); - Console.WriteLine ("Hint: set the TOOPHER_CONSUMER_SECRET and TOOPHER_CONSUMER_SECRET environment variables to avoid this prompt."); - Console.Write ("Consumer key: "); + Console.WriteLine ("Hint: Set the TOOPHER_CONSUMER_SECRET and TOOPHER_CONSUMER_SECRET environment variables to avoid this prompt."); + Console.Write ("Consumer Key: "); consumerKey = Console.ReadLine (); - Console.Write ("Consumer secret: "); + Console.Write ("Consumer Secret: "); consumerSecret = Console.ReadLine (); } string baseUrl = System.Environment.GetEnvironmentVariable ("TOOPHER_BASE_URL"); api = new Toopher.ToopherApi (consumerKey, consumerSecret, baseUrl); + Pairing pairing; string pairingId; while (true) { string pairingPhrase; @@ -42,7 +45,7 @@ public static void Main (string[] args) Console.Write ("Enter pairing phrase: "); pairingPhrase = Console.ReadLine (); - if (pairingPhrase.Length == 0) { + if (string.IsNullOrEmpty(pairingPhrase.Trim())) { Console.WriteLine ("Please enter a pairing phrase to continue"); } else { break; @@ -51,18 +54,18 @@ public static void Main (string[] args) Console.Write (String.Format ("Enter a username for this pairing [{0}]: ", DEFAULT_USERNAME)); string userName = Console.ReadLine (); - if (userName.Length == 0) { + if (string.IsNullOrEmpty(userName.Trim())) { userName = DEFAULT_USERNAME; } Console.WriteLine ("Sending pairing request..."); try { - var pairing = api.Pair (pairingPhrase, userName); + pairing = api.Pair (userName, pairingPhrase); pairingId = pairing.id; break; } catch (RequestError err) { - System.Console.WriteLine (String.Format ("The pairing phrase was not accepted (reason:{0})", err.Message)); + System.Console.WriteLine (String.Format ("The pairing phrase was not accepted (Reason: {0})", err.Message)); } } @@ -72,12 +75,15 @@ public static void Main (string[] args) Console.WriteLine ("Checking status of pairing request..."); try { - var pairing = api.advanced.pairings.GetById (pairingId); - if (pairing.enabled) { + pairing.RefreshFromServer(); + if (pairing.pending) { + Console.WriteLine ("The pairing has not been authorized by the phone yet."); + } else if (pairing.enabled) { Console.WriteLine ("Pairing complete"); break; } else { - Console.WriteLine ("The pairing has not been authorized by the phone yet."); + Console.WriteLine ("The pairing has been denied."); + Environment.Exit(0); } } catch (RequestError err) { Console.WriteLine (String.Format ("Could not check pairing status (reason:{0})", err.Message)); @@ -89,16 +95,15 @@ public static void Main (string[] args) Console.WriteLine ("--------------------------------------"); Console.Write (String.Format ("Enter a terminal name for this authentication request [\"{0}\"]: ", DEFAULT_TERMINAL_NAME)); string terminalName = Console.ReadLine (); - if (terminalName.Length == 0) { + if (string.IsNullOrEmpty(terminalName.Trim())) { terminalName = DEFAULT_TERMINAL_NAME; } Console.WriteLine ("Sending authentication request..."); - string requestId; + AuthenticationRequest authRequest; try { - var requestStatus = api.Authenticate (pairingId, terminalName); - requestId = requestStatus.id; + authRequest = api.Authenticate (pairingId, terminalName); } catch (RequestError err) { Console.WriteLine (String.Format ("Error initiating authentication (reason:{0})", err.Message)); continue; @@ -109,21 +114,20 @@ public static void Main (string[] args) Console.ReadLine (); Console.WriteLine ("Checking status of authentication request..."); - AuthenticationRequest requestStatus; try { - requestStatus = api.GetAuthenticationRequest (requestId); + authRequest.RefreshFromServer(); } catch (RequestError err) { Console.WriteLine (String.Format ("Could not check authentication status (reason:{0})", err.Message)); continue; } - if (requestStatus.pending) { + if (authRequest.pending) { Console.WriteLine ("The authentication request has not received a response from the phone yet."); } else { - string automation = requestStatus.automated ? "automatically " : ""; - string result = requestStatus.granted ? "granted" : "denied"; + string automation = authRequest.automated ? "automatically " : ""; + string result = authRequest.granted ? "granted" : "denied"; Console.WriteLine ("The request was " + automation + result + "!"); - Console.WriteLine ("This request " + ((bool)requestStatus["totp_valid"] ? "had" : "DID NOT HAVE") + " a valid authenticator OTP."); + Console.WriteLine ("This request " + ((bool)authRequest["totp_valid"] ? "had" : "DID NOT HAVE") + " a valid authenticator OTP."); break; } } From f24c95de9931cc0cf857b53ffcd13ce500c00fcd Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Feb 2015 16:14:53 -0600 Subject: [PATCH 066/103] Update ToopherZeroStorageDemo --- .../ToopherZeroStorageDemo.cs | 79 ++++++++++--------- 1 file changed, 43 insertions(+), 36 deletions(-) diff --git a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs index 7edd988..c90a08b 100644 --- a/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs +++ b/ToopherZeroStorageDemo/ToopherZeroStorageDemo.cs @@ -50,9 +50,9 @@ public static void Main (string[] args) Console.WriteLine ("--------------------------------------"); Console.WriteLine ("Enter your requester credentials (from https://dev.toopher.com)."); Console.WriteLine ("Hint: set the TOOPHER_CONSUMER_SECRET and TOOPHER_CONSUMER_SECRET environment variables to avoid this prompt."); - Console.Write ("Consumer key: "); + Console.Write ("Consumer Key: "); consumerKey = Console.ReadLine (); - Console.Write ("Consumer secret: "); + Console.Write ("Consumer Secret: "); consumerSecret = Console.ReadLine (); } string baseUrl = System.Environment.GetEnvironmentVariable ("TOOPHER_BASE_URL"); @@ -61,31 +61,31 @@ public static void Main (string[] args) Console.Write(String.Format("Enter a username to authenticate with Toopher [{0}]: ", DEFAULT_USERNAME)); string userName = Console.ReadLine (); - if (userName.Length == 0) { + if (string.IsNullOrEmpty(userName.Trim())) { userName = DEFAULT_USERNAME; } - string terminalIdentifier = random.Next().ToString(); + string requesterSpecifiedId = random.Next().ToString(); state = STATE.AUTHENTICATE; Pairing pairing = null; - AuthenticationRequest authStatus = null; + AuthenticationRequest authRequest = null; while (true) { switch (state) { case STATE.AUTHENTICATE: { try { Console.WriteLine (String.Format ("\n\nAuthenticating user \"{0}\"", userName)); - authStatus = api.Authenticate (userName, terminalIdentifier); + authRequest = api.Authenticate (userName, requesterSpecifiedId); state = STATE.EVALUATE_AUTHENTICATION_STATUS; - } catch (UserDisabledError e) { + } catch (UserDisabledError) { state = STATE.USER_DISABLED; - } catch (UserUnknownError e) { + } catch (UserUnknownError) { Console.WriteLine ("That username is not yet paired."); state = STATE.PAIR; - } catch (TerminalUnknownError e) { + } catch (TerminalUnknownError) { Console.WriteLine ("First attempt to authenticate from an unknown terminal."); state = STATE.NAME_TERMINAL; - } catch (PairingDeactivatedError e) { + } catch (PairingDeactivatedError) { Console.WriteLine("The pairing has been deactivatied."); state = STATE.PAIR; } catch (RequestError e) { @@ -103,49 +103,49 @@ public static void Main (string[] args) if (response == "2") { state = STATE.ENTER_OTP; } else if (response == "3") { - state = STATE.RESET_PAIRING; + state = STATE.RESET_PAIRING; } else { Console.WriteLine ("Checking status of authentication request..."); try { - authStatus = api.GetAuthenticationRequest (authStatus.id); + authRequest.RefreshFromServer(); state = STATE.EVALUATE_AUTHENTICATION_STATUS; } catch (RequestError err) { Console.WriteLine (String.Format ("Could not check authentication status (reason:{0})", err.Message)); } } - + break; }; case STATE.EVALUATE_AUTHENTICATION_STATUS: { - if (authStatus.pending) { + if (authRequest.pending) { Console.WriteLine ("The authentication request has not received a response from the phone yet."); state = STATE.POLL_FOR_AUTHENTICATION; } else { - string automation = authStatus.automated ? "automatically " : ""; - string result = authStatus.granted ? "granted" : "denied"; + string automation = authRequest.automated ? "automatically " : ""; + string result = authRequest.granted ? "granted" : "denied"; Console.WriteLine ("The request was " + automation + result + "!"); - Console.WriteLine ("This request " + ((bool)authStatus["totp_valid"] ? "had" : "DID NOT HAVE") + " a valid authenticator OTP."); + Console.WriteLine ("This request " + ((bool)authRequest["totp_valid"] ? "had" : "DID NOT HAVE") + " a valid authenticator OTP."); Console.WriteLine (); if (yesNoPrompt("Simulate moving to a new terminal for next authentication request? (Ctrl-C to Exit) ", "N")){ - // generate a new random terminalIdentifier - terminalIdentifier = random.Next ().ToString (); + // generate a new random requesterSpecifiedId + requesterSpecifiedId = random.Next ().ToString (); } state = STATE.AUTHENTICATE; } - break; + break; }; case STATE.ENTER_OTP: { - Console.Write ("Please enter the Pairing OTP value generated in the Toopher Mobile App: "); + Console.Write ("Please enter the pairing OTP value generated in the Toopher Mobile App: "); string otp = Console.ReadLine ().Trim (); - authStatus = api.GetAuthenticationRequest (authStatus.id, otp: otp); + authRequest.GrantWithOtp (otp); state = STATE.EVALUATE_AUTHENTICATION_STATUS; break; }; case STATE.USER_DISABLED: { if(yesNoPrompt("Toopher Authentication is disabled for that user. Do you want to enable Toopher?", "Y")) { - api.SetToopherEnabledForUser (userName, true); + authRequest.user.EnableToopherAuthentication (); } state = STATE.AUTHENTICATE; break; @@ -159,7 +159,7 @@ public static void Main (string[] args) Console.WriteLine ("Pairing phrases are generated on the mobile app"); Console.Write ("Enter pairing phrase: "); pairingPhrase = Console.ReadLine (); - if (pairingPhrase.Length == 0) { + if (string.IsNullOrEmpty(pairingPhrase.Trim())) { Console.WriteLine ("Please enter a pairing phrase to continue"); } else { break; @@ -167,7 +167,7 @@ public static void Main (string[] args) } try { - pairing = api.Pair (pairingPhrase, userName); + pairing = api.Pair (userName, pairingPhrase); state = STATE.POLL_FOR_PAIRING; break; } catch (RequestError err) { @@ -176,12 +176,14 @@ public static void Main (string[] args) break; }; case STATE.POLL_FOR_PAIRING: { - pairing = api.advanced.pairings.GetById (pairing.id); - if (pairing.enabled) { + pairing.RefreshFromServer(); + if (pairing.pending) { + Console.WriteLine ("The pairing has not been authorized by the phone yet."); + } else if (pairing.enabled) { Console.WriteLine ("Pairing complete"); state = STATE.AUTHENTICATE; } else { - Console.WriteLine ("The pairing has not been authorized by the phone yet."); + Console.WriteLine ("The pairing has been denied."); } break; }; @@ -194,7 +196,12 @@ public static void Main (string[] args) Console.Write ("Answer : "); securityAnswer = Console.ReadLine ().Trim (); } - string reset_url = api.GetPairingResetLink (((IDictionary)authStatus["pairing"])["id"].ToString (), securityQuestion, securityAnswer); + Dictionary extras = new Dictionary() + { + {"security_question", securityQuestion}, + {"security_answer", securityAnswer} + }; + string reset_url = pairing.GetResetLink (extras); Console.WriteLine (String.Format ("Created a One-Time Pairing Reset link: {0}", reset_url)); if (yesNoPrompt ("Would you like to open this link in a browser?", "Y")) { System.Diagnostics.Process.Start (reset_url); @@ -202,19 +209,19 @@ public static void Main (string[] args) Console.WriteLine ("Press [Enter] to continue..."); Console.ReadLine (); state = STATE.AUTHENTICATE; - + break; }; case STATE.NAME_TERMINAL: { Console.Write (String.Format ("Enter a terminal name for this authentication request [\"{0}\"]: ", DEFAULT_TERMINAL_NAME)); string terminalName = Console.ReadLine (); - if (terminalName.Length == 0) { + if (string.IsNullOrEmpty(terminalName.Trim())) { terminalName = DEFAULT_TERMINAL_NAME; } try { - api.CreateUserTerminal(userName, terminalName, terminalIdentifier); + api.advanced.userTerminals.Create(userName, terminalName, requesterSpecifiedId); } catch(RequestError e) { - Console.WriteLine (String.Format ("could not create terminal (reason:{0})", e.Message)); + Console.WriteLine (String.Format ("Could not create terminal (reason:{0})", e.Message)); } state = STATE.AUTHENTICATE; break; @@ -222,13 +229,13 @@ public static void Main (string[] args) default: { Console.WriteLine (String.Format ("Unknown state {0}", state)); state = STATE.AUTHENTICATE; - break; + break; }; } } - + } - + } } From e87832cd62abded27ea3e74907b1827786ba2724 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 19 Feb 2015 09:29:41 -0600 Subject: [PATCH 067/103] Update README with new api.Pair() and api.Authenticate() --- README.md | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index f79995f..b85a264 100644 --- a/README.md +++ b/README.md @@ -32,13 +32,21 @@ using Toopher; ToopherApi api = new ToopherApi("", ""); // Step 1 - Pair with their phone's Toopher app -Pairing pairing = api.Pair("pairing phrase", "username@yourservice.com"); +// With pairing phrase +Pairing pairing = api.Pair("username@yourservice.com", "pairing phrase"); +// With SMS +Pairing pairing = api.Pair("username@yourservice.com", "555-555-5555"); +// With QR code +Pairing pairing = api.Pair("username@yourservice.com"); // Step 2 - Authenticate a log in +// With a pairing id AuthenticationRequest auth = api.Authenticate(pairing.id, "my computer"); +// With a username +AuthenticationRequest auth = api.Authenticate("username", "requesterSpecifiedId"); // Once they've responded you can then check the status -AuthenticationRequest status = api.GetAuthenticationRequest(auth.id); +auth.RefreshFromServer(); if (status.pending == false && status.granted == true) { // Success! } From 1cbc151929aff634cd047ab8d5c643ebf0f040a8 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 19 Feb 2015 14:53:39 -0600 Subject: [PATCH 068/103] Make corrections in README --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index b85a264..09dfe81 100644 --- a/README.md +++ b/README.md @@ -40,14 +40,14 @@ Pairing pairing = api.Pair("username@yourservice.com", "555-555-5555"); Pairing pairing = api.Pair("username@yourservice.com"); // Step 2 - Authenticate a log in -// With a pairing id +// With a pairing id and terminal name AuthenticationRequest auth = api.Authenticate(pairing.id, "my computer"); -// With a username +// With a username and requester specified terminal id AuthenticationRequest auth = api.Authenticate("username", "requesterSpecifiedId"); // Once they've responded you can then check the status auth.RefreshFromServer(); -if (status.pending == false && status.granted == true) { +if (auth.pending == false && auth.granted == true) { // Success! } ``` From 3a2ac0ec089c5e9abf0ad176cffb32b71c95a95b Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 19 Feb 2015 16:06:50 -0600 Subject: [PATCH 069/103] Return new User from Users.getByName instead of getById --- ToopherDotNet/ToopherDotNet.cs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 05e71c7..eae0d3e 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -449,9 +449,7 @@ public User GetByName (string userName) if (json.Count() == 0) { throw new RequestError (string.Format ("No users with name {0}", userName)); } - var user = (JsonObject)json[0]; - string userId = user["id"].ToString (); - return GetById (userId); + return new User((JsonObject)json[0], api); } /// From 776b27a64bec2e8104ae59096f3236544c213438 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 19 Feb 2015 16:57:15 -0600 Subject: [PATCH 070/103] Used named parameters for ToopherApi.Authenticate --- README.md | 2 +- ToopherDotNet/ToopherDotNet.cs | 10 +++++++--- ToopherDotNetTests/ToopherDotNetTests.cs | 12 ++++++------ 3 files changed, 14 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 09dfe81..5ce96e7 100644 --- a/README.md +++ b/README.md @@ -43,7 +43,7 @@ Pairing pairing = api.Pair("username@yourservice.com"); // With a pairing id and terminal name AuthenticationRequest auth = api.Authenticate(pairing.id, "my computer"); // With a username and requester specified terminal id -AuthenticationRequest auth = api.Authenticate("username", "requesterSpecifiedId"); +AuthenticationRequest auth = api.Authenticate("username", requesterSpecifiedId: "requesterSpecifiedId"); // Once they've responded you can then check the status auth.RefreshFromServer(); diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index eae0d3e..511c346 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -320,7 +320,7 @@ public Pairing Pair (string userName, string pairingPhraseOrNum = null, Dictiona /// The user-facing descriptive name for the action which is being authenticated. /// An optional Dictionary of extra parameters to provide to the API. /// An object. - public AuthenticationRequest Authenticate (string pairingIdOrUsername, string terminalNameOrRequesterSpecifiedId, string actionName = null, Dictionary extras = null) + public AuthenticationRequest Authenticate (string pairingIdOrUsername, string terminalName = null, string requesterSpecifiedId = null, string actionName = null, Dictionary extras = null) { string endpoint = "authentication_requests/initiate"; NameValueCollection parameters = new NameValueCollection (); @@ -328,12 +328,16 @@ public AuthenticationRequest Authenticate (string pairingIdOrUsername, string te try { new Guid(pairingIdOrUsername); parameters.Add ("pairing_id", pairingIdOrUsername); - parameters.Add ("terminal_name", terminalNameOrRequesterSpecifiedId); } catch (Exception) { parameters.Add ("user_name", pairingIdOrUsername); - parameters.Add ("requester_specified_terminal_id", terminalNameOrRequesterSpecifiedId); } + if (terminalName != null && terminalName.Length > 0) { + parameters.Add ("terminal_name", terminalName); + } + if (requesterSpecifiedId != null && requesterSpecifiedId.Length > 0) { + parameters.Add ("requester_specified_terminal_id", requesterSpecifiedId); + } if (actionName != null && actionName.Length > 0) { parameters.Add ("action_name", actionName); } diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 1b803d7..90f28a8 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -375,7 +375,7 @@ public void AuthenticateWithUsernameAndExtrasTest () { var api = getToopherApi (); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; - AuthenticationRequest auth = api.Authenticate ("some other user", "requester specified id", extras: new Dictionary() {{ "random_key" , "42" }}); + AuthenticationRequest auth = api.Authenticate ("some other user", requesterSpecifiedId: "requester specified id", extras: new Dictionary() {{ "random_key" , "42" }}); Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some other user"); Assert.AreEqual (WebClientMock.LastRequestData["requester_specified_terminal_id"], "requester specified id"); @@ -626,7 +626,7 @@ public void DisabledUserRaisesCorrectErrorTest () var api = getToopherApi (); WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":704, ""error_message"":""The specified user has disabled Toopher authentication.""}"); - api.Authenticate ("some disabled user", "some random string"); + api.Authenticate ("some disabled user", requesterSpecifiedId: "some random string"); } [Test] @@ -636,7 +636,7 @@ public void UnknownUserRaisesCorrectErrorTest () var api = getToopherApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":705, ""error_message"":""No matching user exists.""}"); - api.Authenticate ("some unknown user", "some random string"); + api.Authenticate ("some unknown user", requesterSpecifiedId: "some random string"); } [Test] @@ -646,7 +646,7 @@ public void UnknownTerminalRaisesCorrectErrorTest () var api = getToopherApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":706, ""error_message"":""No matching terminal exists.""}"); - api.Authenticate ("some unknown user", "some random string"); + api.Authenticate ("some unknown user", requesterSpecifiedId: "some random string"); } [Test] @@ -656,7 +656,7 @@ public void DeactivatedPairingRaisesCorrectErrorTest () var api = getToopherApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":601, ""error_message"":""This pairing has been deactivated.""}"); - api.Authenticate ("some disabled user", "some random string"); + api.Authenticate ("some disabled user", requesterSpecifiedId: "some random string"); } [Test] @@ -666,7 +666,7 @@ public void UnauthorizedPairingRaisesCorrectErrorTest () var api = getToopherApi (); WebClientMock.ReturnException = makeError ((HttpStatusCode)409, @"{""error_code"":601, ""error_message"":""This pairing has not been authorized to authenticate.""}"); - api.Authenticate ("some unauthorized user", "some random string"); + api.Authenticate ("some unauthorized user", requesterSpecifiedId: "some random string"); } } From 7c5875a6e698f0b280adeedddc64c94df7f56224 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Fri, 20 Feb 2015 12:29:24 -0600 Subject: [PATCH 071/103] Add ToopherObjectFactory for Users, UserTerminals, AuthenticationRequests and Pairings --- ToopherDotNet/ToopherDotNet.cs | 42 ++++++++++++++++++---------------- 1 file changed, 22 insertions(+), 20 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 511c346..9e9160c 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -352,6 +352,20 @@ public AuthenticationRequest Authenticate (string pairingIdOrUsername, string te return new AuthenticationRequest (json, this); } + public class ToopherObjectFactory + { + public ToopherApi api + { + get; + private set; + } + + public ToopherObjectFactory (ToopherApi toopherApi) + { + this.api = toopherApi; + } + } + public class AdvancedApiUsageFactory { public ToopherApi.AdvancedApiUsageFactory.Pairings pairings; @@ -369,13 +383,10 @@ public AdvancedApiUsageFactory (ToopherApi toopherApi) this.raw = new ToopherApi.AdvancedApiUsageFactory.ApiRawRequester(toopherApi); } - public class Pairings + public class Pairings : ToopherObjectFactory { - private ToopherApi api; - - public Pairings (ToopherApi toopherApi) + public Pairings (ToopherApi toopherApi) : base(toopherApi) { - this.api = toopherApi; } /// @@ -391,13 +402,10 @@ public Pairing GetById (string pairingId) } } - public class AuthenticationRequests + public class AuthenticationRequests : ToopherObjectFactory { - private ToopherApi api; - - public AuthenticationRequests (ToopherApi toopherApi) + public AuthenticationRequests (ToopherApi toopherApi) : base(toopherApi) { - this.api = toopherApi; } /// @@ -413,13 +421,10 @@ public AuthenticationRequest GetById (string authenticationRequestId) } } - public class Users + public class Users : ToopherObjectFactory { - private ToopherApi api; - - public Users (ToopherApi toopherApi) + public Users (ToopherApi toopherApi) : base(toopherApi) { - this.api = toopherApi; } /// @@ -474,13 +479,10 @@ public User Create (string userName, NameValueCollection parameters = null) } } - public class UserTerminals + public class UserTerminals : ToopherObjectFactory { - private ToopherApi api; - - public UserTerminals (ToopherApi toopherApi) + public UserTerminals (ToopherApi toopherApi) : base(toopherApi) { - this.api = toopherApi; } /// From df4eeaff5d3b26920b906023fde7fcaf218db64f Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Mon, 23 Feb 2015 11:53:44 -0600 Subject: [PATCH 072/103] Cleanup ToopherIframe.Signature --- ToopherDotNet/ToopherDotNet.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 9e9160c..edf101a 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -166,7 +166,7 @@ public Dictionary ValidatePostback (Dictionary data.Remove("toopher_sig"); var signatureValid = false; try { - var computedSig = Signature(consumerSecret, maybeSignature, data); + var computedSig = Signature(consumerSecret, data); signatureValid = computedSig == maybeSignature; } catch (Exception e) { throw new SignatureValidationError ("Error while calculating signature: " + e); @@ -203,15 +203,15 @@ private static int GetUnixEpochTimeInSeconds () return (int) t.TotalSeconds; } - private static string Signature (string secret, string maybeSignature, Dictionary data) + private static string Signature (string secret, Dictionary data) { Dictionary sortedData = data.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); string joinedString = string.Join("&", (sortedData.Select(d => d.Key + "=" + d.Value).ToArray())); - byte[] keyByte = Encoding.UTF8.GetBytes(secret); + byte[] keyBytes = Encoding.UTF8.GetBytes(secret); byte[] messageBytes = Encoding.UTF8.GetBytes(joinedString); - using (var hmac = new HMACSHA1(keyByte)) + using (var hmac = new HMACSHA1(keyBytes)) { byte[] hashMessage = hmac.ComputeHash(messageBytes); return Convert.ToBase64String(hashMessage); From 698c93adabba8226eeed2cbd57ff8902301ae7e5 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Feb 2015 13:20:49 -0600 Subject: [PATCH 073/103] Cleanup demo --- ToopherDotNetDemo/ToopherDotNetDemo.cs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/ToopherDotNetDemo/ToopherDotNetDemo.cs b/ToopherDotNetDemo/ToopherDotNetDemo.cs index a2ac8ba..4663951 100644 --- a/ToopherDotNetDemo/ToopherDotNetDemo.cs +++ b/ToopherDotNetDemo/ToopherDotNetDemo.cs @@ -86,7 +86,7 @@ public static void Main (string[] args) Environment.Exit(0); } } catch (RequestError err) { - Console.WriteLine (String.Format ("Could not check pairing status (reason:{0})", err.Message)); + Console.WriteLine (String.Format ("Could not check pairing status (Reason:{0})", err.Message)); } } @@ -103,9 +103,9 @@ public static void Main (string[] args) AuthenticationRequest authRequest; try { - authRequest = api.Authenticate (pairingId, terminalName); + authRequest = api.Authenticate (pairing.user.name, terminalName); } catch (RequestError err) { - Console.WriteLine (String.Format ("Error initiating authentication (reason:{0})", err.Message)); + Console.WriteLine (String.Format ("Error initiating authentication (Reason:{0})", err.Message)); continue; } @@ -117,7 +117,7 @@ public static void Main (string[] args) try { authRequest.RefreshFromServer(); } catch (RequestError err) { - Console.WriteLine (String.Format ("Could not check authentication status (reason:{0})", err.Message)); + Console.WriteLine (String.Format ("Could not check authentication status (Reason:{0})", err.Message)); continue; } From b26a278b620299ff6ee4752dee5f866ce75a77ed Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Feb 2015 13:23:14 -0600 Subject: [PATCH 074/103] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 5ce96e7..e619bde 100644 --- a/README.md +++ b/README.md @@ -42,8 +42,8 @@ Pairing pairing = api.Pair("username@yourservice.com"); // Step 2 - Authenticate a log in // With a pairing id and terminal name AuthenticationRequest auth = api.Authenticate(pairing.id, "my computer"); -// With a username and requester specified terminal id -AuthenticationRequest auth = api.Authenticate("username", requesterSpecifiedId: "requesterSpecifiedId"); +// With a username and terminal name and/or requester specified terminal id +AuthenticationRequest auth = api.Authenticate("username", terminalName: "terminalName", requesterSpecifiedId: "requesterSpecifiedId"); // Once they've responded you can then check the status auth.RefreshFromServer(); From 219a8af59db3cf26e4bdc2858688a56229d1bf04 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Feb 2015 13:24:14 -0600 Subject: [PATCH 075/103] Fix typo in test assertion message --- ToopherDotNetTests/ToopherDotNetTests.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 90f28a8..850bbc9 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -203,7 +203,7 @@ public void ValidatePostbackWithGoodSignatureIsSuccessfulTest () try { Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, 5)); } catch (Exception) { - Assert.Fail("Valid signture, timestamp, and session token did not return validated data"); + Assert.Fail("Valid signature, timestamp, and session token did not return validated data"); } } From 564e4869a9d8e3cf59d1becd387da93e2f84c0b9 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Feb 2015 13:28:57 -0600 Subject: [PATCH 076/103] Add instructions for running demo --- README.md | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index e619bde..2746184 100644 --- a/README.md +++ b/README.md @@ -57,4 +57,9 @@ if (auth.pending == false && auth.granted == true) { If any request runs into an error a `RequestError` will be thrown with more details on what went wrong. #### Try it out -Check out the `ToopherDotNetDemo` project for an example program that walks you through the whole process! +Check out the ToopherDotNetDemo for an example program that walks you through the whole process! Simply execute the script as follows: +```shell +$ xbuild +$ mono ToopherDotNetDemo/bin/Debug/ToopherDotNetDemo.exe +``` +To avoid being prompted for your Toopher API key and secret, you can define them in the $TOOPHER_CONSUMER_KEY and $TOOPHER_CONSUMER_SECRET environment variables From 6e98c5880400415757d71c8ece751fcce14a687e Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Feb 2015 16:19:12 -0600 Subject: [PATCH 077/103] Fix spacing and braces to follow C# conventions --- ToopherDotNet/ToopherDotNet.cs | 741 +++++++++++---------- ToopherDotNetTests/ToopherDotNetTests.cs | 794 ++++++++++++----------- 2 files changed, 816 insertions(+), 719 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index edf101a..b2d6413 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -29,12 +29,12 @@ public class ToopherIframe private string consumerSecret; private static DateTime? dateOverride; - public static void SetDateOverride (DateTime? dateOverride) + public static void SetDateOverride(DateTime? dateOverride) { ToopherIframe.dateOverride = dateOverride; } - private static DateTime GetDate () + private static DateTime GetDate() { return dateOverride ?? DateTime.UtcNow; } @@ -45,13 +45,15 @@ private static DateTime GetDate () /// Your Toopher API OAuth Consumer Key. /// Your Toopher API OAuth Consumer Secret. /// The base URL of the Toopher API to target. If blank, the default is "https://api.toopher.com/v1/". - public ToopherIframe (string consumerKey, string consumerSecret, string baseUrl = null) + public ToopherIframe(string consumerKey, string consumerSecret, string baseUrl = null) { this.consumerKey = consumerKey; this.consumerSecret = consumerSecret; - if (baseUrl != null) { + if (baseUrl != null) + { this.baseUrl = baseUrl; - } else { + } else + { this.baseUrl = DEFAULT_BASE_URL; } } @@ -66,31 +68,35 @@ public ToopherIframe (string consumerKey, string consumerSecret, string baseUrl /// Optional, can be empty. Toopher will include this value in the signed data returned with the iFrame response. /// An optional Dictionary of extra parameters to provide to the API. /// A string URL that can be used to retrieve the Authentication iFrame by the user's browser. - public string GetAuthenticationUrl (string userName, string resetEmail, string requestToken, string actionName = "Log In", string requesterMetadata = "None", Dictionary extras = null) + public string GetAuthenticationUrl(string userName, string resetEmail, string requestToken, string actionName = "Log In", string requesterMetadata = "None", Dictionary extras = null) { - NameValueCollection parameters = new NameValueCollection (); + NameValueCollection parameters = new NameValueCollection(); long ttl; - if (extras != null && extras.ContainsKey("ttl")) { + if (extras != null && extras.ContainsKey("ttl")) + { ttl = Convert.ToInt64(extras["ttl"]); - extras.Remove ("ttl"); - } else { + extras.Remove("ttl"); + } else + { ttl = DEFAULT_TTL; } - parameters.Add ("v", IFRAME_VERSION); - parameters.Add ("username", userName); - parameters.Add ("reset_email", resetEmail); - parameters.Add ("session_token", requestToken); - parameters.Add ("action_name", actionName); - parameters.Add ("requester_metadata", requesterMetadata); - parameters.Add ("expires", (GetUnixEpochTimeInSeconds() + ttl).ToString()); - - if (extras != null) { - foreach (KeyValuePair kvp in extras) { - parameters.Add (kvp.Key, kvp.Value); + parameters.Add("v", IFRAME_VERSION); + parameters.Add("username", userName); + parameters.Add("reset_email", resetEmail); + parameters.Add("session_token", requestToken); + parameters.Add("action_name", actionName); + parameters.Add("requester_metadata", requesterMetadata); + parameters.Add("expires", (GetUnixEpochTimeInSeconds() + ttl).ToString()); + + if (extras != null) + { + foreach (KeyValuePair kvp in extras) + { + parameters.Add(kvp.Key, kvp.Value); } } - return GetOauthUrl (baseUrl + "web/authenticate", parameters); + return GetOauthUrl(baseUrl + "web/authenticate", parameters); } /// @@ -100,28 +106,32 @@ public string GetAuthenticationUrl (string userName, string resetEmail, string r /// Email address that the user has access to. In case the user has lost or cannot access their mobile device, Toopher will send a reset email to this address. /// An optional Dictionary of extra parameters to provide to the API. /// A string URL that can be used to retrieve the Pairing iFrame by the user's browser. - public string GetUserManagementUrl (string userName, string resetEmail, Dictionary extras = null) + public string GetUserManagementUrl(string userName, string resetEmail, Dictionary extras = null) { - NameValueCollection parameters = new NameValueCollection (); + NameValueCollection parameters = new NameValueCollection(); long ttl; - if (extras != null && extras.ContainsKey("ttl")) { + if (extras != null && extras.ContainsKey("ttl")) + { ttl = Convert.ToInt64(extras["ttl"]); - extras.Remove ("ttl"); - } else { + extras.Remove("ttl"); + } else + { ttl = DEFAULT_TTL; } parameters.Add("v", IFRAME_VERSION); parameters.Add("username", userName); parameters.Add("reset_email", resetEmail); - parameters.Add ("expires", (GetUnixEpochTimeInSeconds() + ttl).ToString()); + parameters.Add("expires", (GetUnixEpochTimeInSeconds() + ttl).ToString()); - if (extras != null) { - foreach (KeyValuePair kvp in extras) { - parameters.Add (kvp.Key, kvp.Value); + if (extras != null) + { + foreach (KeyValuePair kvp in extras) + { + parameters.Add(kvp.Key, kvp.Value); } } - return GetOauthUrl (baseUrl + "web/manage_user", parameters); + return GetOauthUrl(baseUrl + "web/manage_user", parameters); } /// @@ -131,79 +141,91 @@ public string GetUserManagementUrl (string userName, string resetEmail, Dictiona /// The session token /// Time-To-Live (seconds) to enforce on the Toopher API signature. This value sets the maximum duration between the Toopher API creating the signature and the signature being validated on your server. /// A Dictionary of the validated data if the signature is valid, or null if the signature is invalid. - public Dictionary ValidatePostback (Dictionary parameters, string sessionToken, long ttl) + public Dictionary ValidatePostback(Dictionary parameters, string sessionToken, long ttl) { - try { - List missingKeys = new List (); + try + { + List missingKeys = new List(); Dictionary data = new Dictionary(); foreach (var entry in parameters) { - if (entry.Value.Length > 0) { + if (entry.Value.Length > 0) + { data.Add(entry.Key, entry.Value[0]); } } - if (!data.ContainsKey("toopher_sig")) { + if (!data.ContainsKey("toopher_sig")) + { missingKeys.Add("toopher_sig"); } - if (!data.ContainsKey("timestamp")) { + if (!data.ContainsKey("timestamp")) + { missingKeys.Add("timestamp"); } - if (!data.ContainsKey("session_token")) { + if (!data.ContainsKey("session_token")) + { missingKeys.Add("session_token"); } - if (missingKeys.Count() > 0) { + if (missingKeys.Count() > 0) + { var keys = string.Join(", ", missingKeys.ToArray()); - throw new SignatureValidationError ("Missing required keys: " + keys); + throw new SignatureValidationError("Missing required keys: " + keys); } - if (data["session_token"] != sessionToken) { - throw new SignatureValidationError ("Session token does not match expected value"); + if (data["session_token"] != sessionToken) + { + throw new SignatureValidationError("Session token does not match expected value"); } string maybeSignature = data["toopher_sig"]; data.Remove("toopher_sig"); var signatureValid = false; - try { + try + { var computedSig = Signature(consumerSecret, data); signatureValid = computedSig == maybeSignature; - } catch (Exception e) { - throw new SignatureValidationError ("Error while calculating signature: " + e); + } catch (Exception e) + { + throw new SignatureValidationError("Error while calculating signature: " + e); } - if (!signatureValid) { - throw new SignatureValidationError ("Computed signature does not match"); + if (!signatureValid) + { + throw new SignatureValidationError("Computed signature does not match"); } - var ttlValid = (int)(GetUnixEpochTimeInSeconds () - ttl) < Int32.Parse(data["timestamp"]); - if (!ttlValid) { - throw new SignatureValidationError ("TTL Expired"); + var ttlValid = (int)(GetUnixEpochTimeInSeconds() - ttl) < Int32.Parse(data["timestamp"]); + if (!ttlValid) + { + throw new SignatureValidationError("TTL Expired"); } return data; - } catch (Exception e) { - throw new SignatureValidationError ("Exception while validating toopher signature: " + e); + } catch (Exception e) + { + throw new SignatureValidationError("Exception while validating toopher signature: " + e); } } - private string GetOauthUrl (string url, NameValueCollection parameters) + private string GetOauthUrl(string url, NameValueCollection parameters) { - OAuthRequest client = OAuthRequest.ForRequestToken (consumerKey, consumerSecret); + OAuthRequest client = OAuthRequest.ForRequestToken(consumerKey, consumerSecret); client.RequestUrl = url; - string oauthParams = client.GetAuthorizationQuery (parameters); - string requestParams = UrlEncodeParameters (parameters); + string oauthParams = client.GetAuthorizationQuery(parameters); + string requestParams = UrlEncodeParameters(parameters); return url + "?" + requestParams + "&" + oauthParams; } - private static int GetUnixEpochTimeInSeconds () + private static int GetUnixEpochTimeInSeconds() { TimeSpan t = (GetDate() - new DateTime(1970, 1, 1)); - return (int) t.TotalSeconds; + return (int)t.TotalSeconds; } - private static string Signature (string secret, Dictionary data) + private static string Signature(string secret, Dictionary data) { Dictionary sortedData = data.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); string joinedString = string.Join("&", (sortedData.Select(d => d.Key + "=" + d.Value).ToArray())); @@ -218,7 +240,7 @@ private static string Signature (string secret, Dictionary data) } } - private static string UrlEncodeParameters (NameValueCollection parameters) + private static string UrlEncodeParameters(NameValueCollection parameters) { WebParameterCollection collection = new WebParameterCollection(parameters); foreach (var parameter in collection) @@ -232,7 +254,7 @@ private static string UrlEncodeParameters (NameValueCollection parameters) public class SignatureValidationError: Exception { - public SignatureValidationError (string message): base(message) + public SignatureValidationError(string message): base(message) { } } @@ -255,19 +277,23 @@ public class ToopherApi /// Your Toopher API OAuth Consumer Secret /// The base URL of the Toopher API to target. If blank, the default is "https://api.toopher.com/v1/". /// The type of web client. Override WebClient class for testing. If blank, the default is WebClientProxy. - public ToopherApi (string consumerKey, string consumerSecret, string baseUrl = null, Type webClientProxyType = null) + public ToopherApi(string consumerKey, string consumerSecret, string baseUrl = null, Type webClientProxyType = null) { this.advanced = new ToopherApi.AdvancedApiUsageFactory(this); this.consumerKey = consumerKey; this.consumerSecret = consumerSecret; - if (baseUrl != null) { + if (baseUrl != null) + { this.baseUrl = baseUrl; - } else { + } else + { this.baseUrl = ToopherApi.DEFAULT_BASE_URL; } - if (webClientProxyType != null) { + if (webClientProxyType != null) + { this.webClientProxyType = webClientProxyType; - } else { + } else + { this.webClientProxyType = typeof(WebClientProxy); } } @@ -282,33 +308,39 @@ public ToopherApi (string consumerKey, string consumerSecret, string baseUrl = n /// The pairing phrase or phone number supplied by the user. /// An optional Dictionary of extra parameters to provide to the API. /// A object. - public Pairing Pair (string userName, string pairingPhraseOrNum = null, Dictionary extras = null) + public Pairing Pair(string userName, string pairingPhraseOrNum = null, Dictionary extras = null) { string endpoint; - NameValueCollection parameters = new NameValueCollection (); - parameters.Add ("user_name", userName); + NameValueCollection parameters = new NameValueCollection(); + parameters.Add("user_name", userName); - if (pairingPhraseOrNum != null) { - if (Regex.IsMatch(pairingPhraseOrNum, @"[0-9]")) { + if (pairingPhraseOrNum != null) + { + if (Regex.IsMatch(pairingPhraseOrNum, @"[0-9]")) + { parameters.Add("phone_number", pairingPhraseOrNum); endpoint = "pairings/create/sms"; - } else { - parameters.Add ("pairing_phrase", pairingPhraseOrNum); + } else + { + parameters.Add("pairing_phrase", pairingPhraseOrNum); endpoint = "pairings/create"; } - } else { + } else + { endpoint = "pairings/create/qr"; } - if (extras != null) { - foreach (KeyValuePair kvp in extras) { - parameters.Add (kvp.Key, kvp.Value); + if (extras != null) + { + foreach (KeyValuePair kvp in extras) + { + parameters.Add(kvp.Key, kvp.Value); } } - var json = advanced.raw.post (endpoint, parameters); - return new Pairing (json, this); + var json = advanced.raw.post(endpoint, parameters); + return new Pairing(json, this); } /// @@ -320,36 +352,43 @@ public Pairing Pair (string userName, string pairingPhraseOrNum = null, Dictiona /// The user-facing descriptive name for the action which is being authenticated. /// An optional Dictionary of extra parameters to provide to the API. /// An object. - public AuthenticationRequest Authenticate (string pairingIdOrUsername, string terminalName = null, string requesterSpecifiedId = null, string actionName = null, Dictionary extras = null) + public AuthenticationRequest Authenticate(string pairingIdOrUsername, string terminalName = null, string requesterSpecifiedId = null, string actionName = null, Dictionary extras = null) { string endpoint = "authentication_requests/initiate"; - NameValueCollection parameters = new NameValueCollection (); + NameValueCollection parameters = new NameValueCollection(); - try { + try + { new Guid(pairingIdOrUsername); - parameters.Add ("pairing_id", pairingIdOrUsername); - } catch (Exception) { - parameters.Add ("user_name", pairingIdOrUsername); + parameters.Add("pairing_id", pairingIdOrUsername); + } catch (Exception) + { + parameters.Add("user_name", pairingIdOrUsername); } - if (terminalName != null && terminalName.Length > 0) { - parameters.Add ("terminal_name", terminalName); + if (terminalName != null && terminalName.Length > 0) + { + parameters.Add("terminal_name", terminalName); } - if (requesterSpecifiedId != null && requesterSpecifiedId.Length > 0) { - parameters.Add ("requester_specified_terminal_id", requesterSpecifiedId); + if (requesterSpecifiedId != null && requesterSpecifiedId.Length > 0) + { + parameters.Add("requester_specified_terminal_id", requesterSpecifiedId); } - if (actionName != null && actionName.Length > 0) { - parameters.Add ("action_name", actionName); + if (actionName != null && actionName.Length > 0) + { + parameters.Add("action_name", actionName); } - if (extras != null) { - foreach (KeyValuePair kvp in extras) { - parameters.Add (kvp.Key, kvp.Value); + if (extras != null) + { + foreach (KeyValuePair kvp in extras) + { + parameters.Add(kvp.Key, kvp.Value); } } - var json = advanced.raw.post (endpoint, parameters); - return new AuthenticationRequest (json, this); + var json = advanced.raw.post(endpoint, parameters); + return new AuthenticationRequest(json, this); } public class ToopherObjectFactory @@ -360,7 +399,7 @@ public ToopherApi api private set; } - public ToopherObjectFactory (ToopherApi toopherApi) + public ToopherObjectFactory(ToopherApi toopherApi) { this.api = toopherApi; } @@ -374,7 +413,7 @@ public class AdvancedApiUsageFactory public ToopherApi.AdvancedApiUsageFactory.UserTerminals userTerminals; public ToopherApi.AdvancedApiUsageFactory.ApiRawRequester raw; - public AdvancedApiUsageFactory (ToopherApi toopherApi) + public AdvancedApiUsageFactory(ToopherApi toopherApi) { this.pairings = new ToopherApi.AdvancedApiUsageFactory.Pairings(toopherApi); this.authenticationRequests = new ToopherApi.AdvancedApiUsageFactory.AuthenticationRequests(toopherApi); @@ -385,7 +424,7 @@ public AdvancedApiUsageFactory (ToopherApi toopherApi) public class Pairings : ToopherObjectFactory { - public Pairings (ToopherApi toopherApi) : base(toopherApi) + public Pairings(ToopherApi toopherApi) : base(toopherApi) { } @@ -394,17 +433,17 @@ public Pairings (ToopherApi toopherApi) : base(toopherApi) /// /// The unique id for a pairing. /// A object. - public Pairing GetById (string pairingId) + public Pairing GetById(string pairingId) { - string endpoint = string.Format ("pairings/{0}", pairingId); - var json = api.advanced.raw.get (endpoint); - return new Pairing (json, api); + string endpoint = string.Format("pairings/{0}", pairingId); + var json = api.advanced.raw.get(endpoint); + return new Pairing(json, api); } } public class AuthenticationRequests : ToopherObjectFactory { - public AuthenticationRequests (ToopherApi toopherApi) : base(toopherApi) + public AuthenticationRequests(ToopherApi toopherApi) : base(toopherApi) { } @@ -413,17 +452,17 @@ public AuthenticationRequests (ToopherApi toopherApi) : base(toopherApi) /// /// The unique id for an authentication request. /// A object. - public AuthenticationRequest GetById (string authenticationRequestId) + public AuthenticationRequest GetById(string authenticationRequestId) { - string endpoint = string.Format ("authentication_requests/{0}", authenticationRequestId); - var json = api.advanced.raw.get (endpoint); - return new AuthenticationRequest (json, api); + string endpoint = string.Format("authentication_requests/{0}", authenticationRequestId); + var json = api.advanced.raw.get(endpoint); + return new AuthenticationRequest(json, api); } } public class Users : ToopherObjectFactory { - public Users (ToopherApi toopherApi) : base(toopherApi) + public Users(ToopherApi toopherApi) : base(toopherApi) { } @@ -432,11 +471,11 @@ public Users (ToopherApi toopherApi) : base(toopherApi) /// /// The unique id for a user. /// A object. - public User GetById (string userId) + public User GetById(string userId) { - string endpoint = string.Format ("users/{0}", userId); - var json = api.advanced.raw.get (endpoint); - return new User (json, api); + string endpoint = string.Format("users/{0}", userId); + var json = api.advanced.raw.get(endpoint); + return new User(json, api); } @@ -445,18 +484,20 @@ public User GetById (string userId) /// /// The name of the user. /// A object. - public User GetByName (string userName) + public User GetByName(string userName) { string endpoint = "users"; - NameValueCollection parameters = new NameValueCollection (); - parameters.Add ("name", userName); - JsonArray json = api.advanced.raw.getArray (endpoint, parameters); + NameValueCollection parameters = new NameValueCollection(); + parameters.Add("name", userName); + JsonArray json = api.advanced.raw.getArray(endpoint, parameters); - if (json.Count() > 1) { - throw new RequestError (string.Format ("More than one user with name {0}", userName)); + if (json.Count() > 1) + { + throw new RequestError(string.Format("More than one user with name {0}", userName)); } - if (json.Count() == 0) { - throw new RequestError (string.Format ("No users with name {0}", userName)); + if (json.Count() == 0) + { + throw new RequestError(string.Format("No users with name {0}", userName)); } return new User((JsonObject)json[0], api); } @@ -467,21 +508,22 @@ public User GetByName (string userName) /// The name of the user. /// An optional collection of parameters to provide to the API. /// A object. - public User Create (string userName, NameValueCollection parameters = null) + public User Create(string userName, NameValueCollection parameters = null) { string endpoint = "users/create"; - if (parameters == null) { - parameters = new NameValueCollection (); + if (parameters == null) + { + parameters = new NameValueCollection(); } - parameters.Add ("name", userName); - var json = api.advanced.raw.post (endpoint, parameters); - return new User (json, api); + parameters.Add("name", userName); + var json = api.advanced.raw.post(endpoint, parameters); + return new User(json, api); } } public class UserTerminals : ToopherObjectFactory { - public UserTerminals (ToopherApi toopherApi) : base(toopherApi) + public UserTerminals(ToopherApi toopherApi) : base(toopherApi) { } @@ -490,11 +532,11 @@ public UserTerminals (ToopherApi toopherApi) : base(toopherApi) /// /// The unique id for a user terminal. /// A object. - public UserTerminal GetById (string userTerminalId) + public UserTerminal GetById(string userTerminalId) { - string endpoint = string.Format ("user_terminals/{0}", userTerminalId); - var json = api.advanced.raw.get (endpoint); - return new UserTerminal (json, api); + string endpoint = string.Format("user_terminals/{0}", userTerminalId); + var json = api.advanced.raw.get(endpoint); + return new UserTerminal(json, api); } /// @@ -505,17 +547,18 @@ public UserTerminal GetById (string userTerminalId) /// The requester specified id that uniquely idenfities this terminal. /// An optional collection of parameters to provide to the API. /// A object. - public UserTerminal Create (string userName, string terminalName, string requesterSpecifiedId, NameValueCollection parameters = null) + public UserTerminal Create(string userName, string terminalName, string requesterSpecifiedId, NameValueCollection parameters = null) { string endpoint = "user_terminals/create"; - if (parameters == null) { - parameters = new NameValueCollection (); + if (parameters == null) + { + parameters = new NameValueCollection(); } - parameters.Add ("user_name", userName); - parameters.Add ("terminal_name", terminalName); - parameters.Add ("requester_specified_id", requesterSpecifiedId); - var json = api.advanced.raw.post (endpoint, parameters); - return new UserTerminal (json, api); + parameters.Add("user_name", userName); + parameters.Add("terminal_name", terminalName); + parameters.Add("requester_specified_id", requesterSpecifiedId); + var json = api.advanced.raw.post(endpoint, parameters); + return new UserTerminal(json, api); } } @@ -523,119 +566,140 @@ public class ApiRawRequester { private ToopherApi api; - public ApiRawRequester (ToopherApi toopherApi) + public ApiRawRequester(ToopherApi toopherApi) { this.api = toopherApi; } - private object request (string method, string endpoint, NameValueCollection parameters = null) + private object request(string method, string endpoint, NameValueCollection parameters = null) { // Normalize method string - method = method.ToUpper (); + method = method.ToUpper(); // Build an empty collection for parameters (if necessary) - if (parameters == null) { - parameters = new NameValueCollection (); + if (parameters == null) + { + parameters = new NameValueCollection(); } // can't have null parameters, or oauth signing will barf - foreach (String key in parameters.AllKeys){ - if (parameters[key] == null) { + foreach (String key in parameters.AllKeys) + { + if (parameters[key] == null) + { parameters[key] = ""; } } - var client = OAuthRequest.ForRequestToken (api.consumerKey, api.consumerSecret); + var client = OAuthRequest.ForRequestToken(api.consumerKey, api.consumerSecret); client.RequestUrl = api.baseUrl + endpoint; client.Method = method; - string auth = client.GetAuthorizationHeader (parameters); + string auth = client.GetAuthorizationHeader(parameters); // FIXME: OAuth library puts extraneous comma at end, workaround: remove it if present - auth = auth.TrimEnd (new char[] { ',' }); - - using (WebClientProxy wClient = (WebClientProxy)Activator.CreateInstance(api.webClientProxyType)) { - wClient.Headers.Add ("Authorization", auth); - wClient.Headers.Add ("User-Agent", - string.Format ("Toopher-DotNet/{0} (DotNet {1})", VERSION, Environment.Version.ToString ())); - if (parameters.Count > 0) { + auth = auth.TrimEnd(new char[]{ ',' }); + + using (WebClientProxy wClient = (WebClientProxy)Activator.CreateInstance(api.webClientProxyType)) + { + wClient.Headers.Add("Authorization", auth); + wClient.Headers.Add("User-Agent", + string.Format("Toopher-DotNet/{0} (DotNet {1})", VERSION, Environment.Version.ToString())); + if (parameters.Count > 0) + { wClient.QueryString = parameters; } string response; - try { - if (method.Equals ("POST")) { - var responseArray = wClient.UploadValues (client.RequestUrl, client.Method, parameters); - response = Encoding.UTF8.GetString (responseArray); - } else { - response = wClient.DownloadString (client.RequestUrl); + try + { + if (method.Equals("POST")) + { + var responseArray = wClient.UploadValues(client.RequestUrl, client.Method, parameters); + response = Encoding.UTF8.GetString(responseArray); + } else + { + response = wClient.DownloadString(client.RequestUrl); } - } catch (WebException wex) { + } catch (WebException wex) + { IHttpWebResponse httpResp = HttpWebResponseWrapper.create(wex.Response); string error_message; - using (Stream stream = httpResp.GetResponseStream ()) { - StreamReader reader = new StreamReader (stream, Encoding.UTF8); - error_message = reader.ReadToEnd (); + using (Stream stream = httpResp.GetResponseStream()) + { + StreamReader reader = new StreamReader(stream, Encoding.UTF8); + error_message = reader.ReadToEnd(); } - String statusLine = httpResp.StatusCode.ToString () + " : " + httpResp.StatusDescription; - - if (String.IsNullOrEmpty (error_message)) { - throw new RequestError (statusLine); - } else { + String statusLine = httpResp.StatusCode.ToString() + " : " + httpResp.StatusDescription; - try { + if (String.IsNullOrEmpty(error_message)) + { + throw new RequestError(statusLine); + } else + { + try + { // Attempt to parse JSON response - var json = (JsonObject)SimpleJson.SimpleJson.DeserializeObject (error_message); - parseRequestError (json); - } catch (RequestError e) { + var json = (JsonObject)SimpleJson.SimpleJson.DeserializeObject(error_message); + parseRequestError(json); + } catch (RequestError e) + { throw e; - } catch (Exception) { - throw new RequestError (statusLine + " : " + error_message); + } catch (Exception) + { + throw new RequestError(statusLine + " : " + error_message); } } - throw new RequestError (error_message, wex); + throw new RequestError(error_message, wex); } - try { - return SimpleJson.SimpleJson.DeserializeObject (response); - } catch (Exception ex) { - throw new RequestError ("Could not parse response", ex); + try + { + return SimpleJson.SimpleJson.DeserializeObject(response); + } catch (Exception ex) + { + throw new RequestError("Could not parse response", ex); } } } - public JsonObject get (string endpoint, NameValueCollection parameters = null) + public JsonObject get(string endpoint, NameValueCollection parameters = null) { - return (JsonObject)request ("GET", endpoint, parameters); + return (JsonObject)request("GET", endpoint, parameters); } - public JsonArray getArray (string endpoint, NameValueCollection parameters = null) + public JsonArray getArray(string endpoint, NameValueCollection parameters = null) { - return (JsonArray)request ("GET", endpoint, parameters); + return (JsonArray)request("GET", endpoint, parameters); } - public JsonObject post (string endpoint, NameValueCollection parameters = null) + public JsonObject post(string endpoint, NameValueCollection parameters = null) { - return (JsonObject) request ("POST", endpoint, parameters); + return (JsonObject)request("POST", endpoint, parameters); } - private void parseRequestError (JsonObject err) + private void parseRequestError(JsonObject err) { long errCode = (long)err["error_code"]; string errMessage = (string)err["error_message"]; - if (errCode == UserDisabledError.ERROR_CODE) { - throw new UserDisabledError (); - } else if (errCode == UserUnknownError.ERROR_CODE) { - throw new UserUnknownError (); - } else if (errCode == TerminalUnknownError.ERROR_CODE) { - throw new TerminalUnknownError (); - } else { - if (errMessage.ToLower ().Contains ("pairing has been deactivated") - || errMessage.ToLower ().Contains ("pairing has not been authorized")) { - throw new PairingDeactivatedError (); - } else { - throw new RequestError (errMessage); + if (errCode == UserDisabledError.ERROR_CODE) + { + throw new UserDisabledError(); + } else if (errCode == UserUnknownError.ERROR_CODE) + { + throw new UserUnknownError(); + } else if (errCode == TerminalUnknownError.ERROR_CODE) + { + throw new TerminalUnknownError(); + } else + { + if (errMessage.ToLower().Contains("pairing has been deactivated") || errMessage.ToLower().Contains("pairing has not been authorized")) + { + throw new PairingDeactivatedError(); + } else + { + throw new RequestError(errMessage); } } } @@ -674,9 +738,9 @@ public bool enabled } public User user; - public override string ToString () + public override string ToString() { - return string.Format ("[Pairing: id={0}; enabled={1}; pending={2}; userId={3}; userName={4}; userToopherAuthenticationEnabled={5}]", id, enabled, pending, user.id, user.name, user.toopherAuthenticationEnabled); + return string.Format("[Pairing: id={0}; enabled={1}; pending={2}; userId={3}; userName={4}; userToopherAuthenticationEnabled={5}]", id, enabled, pending, user.id, user.name, user.toopherAuthenticationEnabled); } /// @@ -684,28 +748,30 @@ public override string ToString () /// /// The response from the API. /// The Toopher API associated with this pairing. - public Pairing (IDictionary response, ToopherApi toopherApi) + public Pairing(IDictionary response, ToopherApi toopherApi) { this.rawResponse = response; this.api = toopherApi; - try { + try + { this.id = (string)response["id"]; this.pending = (bool)response["pending"]; this.enabled = (bool)response["enabled"]; - this.user = new User ((JsonObject)response["user"], toopherApi); - } catch (Exception ex) { - throw new RequestError ("Could not parse pairing from response", ex); + this.user = new User((JsonObject)response["user"], toopherApi); + } catch (Exception ex) + { + throw new RequestError("Could not parse pairing from response", ex); } } /// /// Update the pairing object with response from the API. /// - public void RefreshFromServer () + public void RefreshFromServer() { - string endpoint = string.Format ("pairings/{0}", id); - var json = api.advanced.raw.get (endpoint); - Update (json); + string endpoint = string.Format("pairings/{0}", id); + var json = api.advanced.raw.get(endpoint); + Update(json); } /// @@ -713,16 +779,18 @@ public void RefreshFromServer () /// /// An optional Dictionary of extra parameters to provide to the API. /// A reset link as a string - public string GetResetLink (Dictionary extras = null) + public string GetResetLink(Dictionary extras = null) { - string endpoint = string.Format ("pairings/{0}/generate_reset_link", id); - NameValueCollection parameters = new NameValueCollection (); - if (extras != null) { - foreach (KeyValuePair kvp in extras) { - parameters.Add (kvp.Key, kvp.Value); + string endpoint = string.Format("pairings/{0}/generate_reset_link", id); + NameValueCollection parameters = new NameValueCollection(); + if (extras != null) + { + foreach (KeyValuePair kvp in extras) + { + parameters.Add(kvp.Key, kvp.Value); } } - var json = api.advanced.raw.post (endpoint, parameters); + var json = api.advanced.raw.post(endpoint, parameters); return (string)json["url"]; } @@ -731,14 +799,16 @@ public string GetResetLink (Dictionary extras = null) /// /// The email address where the reset link is sent. /// An optional Dictionary of extra parameters to provide to the API. - public void EmailResetLink (string email, Dictionary extras = null) - { - string endpoint = string.Format ("pairings/{0}/send_reset_link", id); - NameValueCollection parameters = new NameValueCollection (); - parameters.Add ("reset_email", email); - if (extras != null) { - foreach (KeyValuePair kvp in extras) { - parameters.Add (kvp.Key, kvp.Value); + public void EmailResetLink(string email, Dictionary extras = null) + { + string endpoint = string.Format("pairings/{0}/send_reset_link", id); + NameValueCollection parameters = new NameValueCollection(); + parameters.Add("reset_email", email); + if (extras != null) + { + foreach (KeyValuePair kvp in extras) + { + parameters.Add(kvp.Key, kvp.Value); } } api.advanced.raw.post(endpoint, parameters); @@ -748,22 +818,24 @@ public void EmailResetLink (string email, Dictionary extras = nu /// Retrieve QR code image for the pairing. /// /// QR code image stored as a byte[]. - public byte[] GetQrCodeImage () + public byte[] GetQrCodeImage() { - string endpoint = string.Format ("qr/pairings/{0}", id); + string endpoint = string.Format("qr/pairings/{0}", id); var result = api.advanced.raw.get(endpoint); - return System.Text.Encoding.UTF8.GetBytes (result.ToString()); + return System.Text.Encoding.UTF8.GetBytes(result.ToString()); } - private void Update (IDictionary response) + private void Update(IDictionary response) { this.rawResponse = response; - try { + try + { this.pending = (bool)response["pending"]; this.enabled = (bool)response["enabled"]; - this.user.Update ((JsonObject)response["user"]); - } catch (Exception ex) { - throw new RequestError ("Could not parse pairing from response", ex); + this.user.Update((JsonObject)response["user"]); + } catch (Exception ex) + { + throw new RequestError("Could not parse pairing from response", ex); } } } @@ -815,9 +887,9 @@ public string reason public UserTerminal terminal; public User user; - public override string ToString () + public override string ToString() { - return string.Format ("[AuthenticationRequest: id={0}; pending={1}; granted={2}; automated={3}; reasonCode={4}; reason={5}; actionId={6}; actionName={7}; terminalId={8}; terminalName={9}; terminalRequesterSpecifiedId={10}; userId={11}; userName={12}; userToopherAuthenticationEnabled={13}]", id, pending, granted, automated, reasonCode, reason, action.id, action.name, terminal.id, terminal.name, terminal.requesterSpecifiedId, user.id, user.name, user.toopherAuthenticationEnabled); + return string.Format("[AuthenticationRequest: id={0}; pending={1}; granted={2}; automated={3}; reasonCode={4}; reason={5}; actionId={6}; actionName={7}; terminalId={8}; terminalName={9}; terminalRequesterSpecifiedId={10}; userId={11}; userName={12}; userToopherAuthenticationEnabled={13}]", id, pending, granted, automated, reasonCode, reason, action.id, action.name, terminal.id, terminal.name, terminal.requesterSpecifiedId, user.id, user.name, user.toopherAuthenticationEnabled); } /// @@ -825,11 +897,12 @@ public override string ToString () /// /// The response from the API. /// The Toopher API associated with this authentication request. - public AuthenticationRequest (IDictionary response, ToopherApi toopherApi) + public AuthenticationRequest(IDictionary response, ToopherApi toopherApi) { this.rawResponse = response; this.api = toopherApi; - try { + try + { this.id = (string)response["id"]; this.pending = (bool)response["pending"]; this.granted = (bool)response["granted"]; @@ -839,38 +912,40 @@ public AuthenticationRequest (IDictionary response, ToopherApi t this.action = new Action((JsonObject)response["action"]); this.terminal = new UserTerminal((JsonObject)response["terminal"], toopherApi); this.user = new User((JsonObject)response["user"], toopherApi); - } catch (Exception ex) { - throw new RequestError ("Could not parse authentication request from response", ex); + } catch (Exception ex) + { + throw new RequestError("Could not parse authentication request from response", ex); } } /// /// Update the authentication request object with response from the API. /// - public void RefreshFromServer () + public void RefreshFromServer() { - string endpoint = string.Format ("authentication_requests/{0}", id); + string endpoint = string.Format("authentication_requests/{0}", id); var json = api.advanced.raw.get(endpoint); - Update (json); + Update(json); } /// /// Grant the authentication request with an OTP. /// /// One-time password for the authentication request. - public void GrantWithOtp (string otp) + public void GrantWithOtp(string otp) { - string endpoint = string.Format ("authentication_requests/{0}/otp_auth", id); - NameValueCollection parameters = new NameValueCollection (); - parameters.Add ("otp", otp); + string endpoint = string.Format("authentication_requests/{0}/otp_auth", id); + NameValueCollection parameters = new NameValueCollection(); + parameters.Add("otp", otp); var json = api.advanced.raw.post(endpoint, parameters); - Update (json); + Update(json); } - private void Update (IDictionary response) + private void Update(IDictionary response) { this.rawResponse = response; - try { + try + { this.pending = (bool)response["pending"]; this.granted = (bool)response["granted"]; this.automated = (bool)response["automated"]; @@ -879,8 +954,9 @@ private void Update (IDictionary response) this.action.Update((JsonObject)response["action"]); this.terminal.Update((JsonObject)response["terminal"]); this.user.Update((JsonObject)response["user"]); - } catch (Exception ex) { - throw new RequestError ("Could not parse authentication request from response", ex); + } catch (Exception ex) + { + throw new RequestError("Could not parse authentication request from response", ex); } } } @@ -914,9 +990,9 @@ public bool toopherAuthenticationEnabled private set; } - public override string ToString () + public override string ToString() { - return string.Format ("[User: id={0}; name={1}; toopherAuthenticationEnabled={2}]", id, name, toopherAuthenticationEnabled); + return string.Format("[User: id={0}; name={1}; toopherAuthenticationEnabled={2}]", id, name, toopherAuthenticationEnabled); } /// @@ -924,54 +1000,58 @@ public override string ToString () /// /// The response from the API. /// The Toopher API associated with this authentication request. - public User (IDictionary response, ToopherApi toopherApi) + public User(IDictionary response, ToopherApi toopherApi) { this.rawResponse = response; this.api = toopherApi; - try { + try + { this.id = (string)response["id"]; this.name = (string)response["name"]; this.toopherAuthenticationEnabled = (bool)response["toopher_authentication_enabled"]; - } catch (Exception ex) { - throw new RequestError ("Could not parse user from response", ex); + } catch (Exception ex) + { + throw new RequestError("Could not parse user from response", ex); } } /// /// Update the user object with response from the API. /// - public void RefreshFromServer () + public void RefreshFromServer() { - string endpoint = string.Format ("users/{0}", id); + string endpoint = string.Format("users/{0}", id); var json = api.advanced.raw.get(endpoint); - Update (json); + Update(json); } /// /// Update the user object with provided response. /// /// The response from the API. - public void Update (IDictionary response) + public void Update(IDictionary response) { this.rawResponse = response; - try { + try + { this.name = (string)response["name"]; this.toopherAuthenticationEnabled = (bool)response["toopher_authentication_enabled"]; - } catch (Exception ex) { - throw new RequestError ("Could not parse user from response", ex); + } catch (Exception ex) + { + throw new RequestError("Could not parse user from response", ex); } } /// /// Enable Toopher Authentication for an individual user. /// - public void EnableToopherAuthentication () + public void EnableToopherAuthentication() { - string endpoint = string.Format ("users/{0}", id); - NameValueCollection parameters = new NameValueCollection (); - parameters.Add ("toopher_authentication_enabled", "true"); - var json = api.advanced.raw.post (endpoint, parameters); - Update (json); + string endpoint = string.Format("users/{0}", id); + NameValueCollection parameters = new NameValueCollection(); + parameters.Add("toopher_authentication_enabled", "true"); + var json = api.advanced.raw.post(endpoint, parameters); + Update(json); } /// @@ -979,23 +1059,23 @@ public void EnableToopherAuthentication () /// disabled, future attempts to authenticate the user with Toopher will return /// a UserDisabledError. /// - public void DisableToopherAuthentication () + public void DisableToopherAuthentication() { - string endpoint = string.Format ("users/{0}", id); - NameValueCollection parameters = new NameValueCollection (); - parameters.Add ("toopher_authentication_enabled", "false"); - var json = api.advanced.raw.post (endpoint, parameters); - Update (json); + string endpoint = string.Format("users/{0}", id); + NameValueCollection parameters = new NameValueCollection(); + parameters.Add("toopher_authentication_enabled", "false"); + var json = api.advanced.raw.post(endpoint, parameters); + Update(json); } /// /// Remove all pairings for the user. /// - public void Reset () + public void Reset() { string endpoint = "users/reset"; - NameValueCollection parameters = new NameValueCollection (); - parameters.Add ("name", name); + NameValueCollection parameters = new NameValueCollection(); + parameters.Add("name", name); api.advanced.raw.post(endpoint, parameters); } } @@ -1030,9 +1110,9 @@ public string requesterSpecifiedId } public User user; - public override string ToString () + public override string ToString() { - return string.Format ("[UserTerminal: id={0}; name={1}; requesterSpecifiedId={2}; userId={3}, userName={4}, userToopherAuthenticationEnabled={5}]", id, name, requesterSpecifiedId, user.id, user.name, user.toopherAuthenticationEnabled); + return string.Format("[UserTerminal: id={0}; name={1}; requesterSpecifiedId={2}; userId={3}, userName={4}, userToopherAuthenticationEnabled={5}]", id, name, requesterSpecifiedId, user.id, user.name, user.toopherAuthenticationEnabled); } /// @@ -1040,43 +1120,47 @@ public override string ToString () /// /// The response from the API. /// The Toopher API associated with this authentication request. - public UserTerminal (IDictionary response, ToopherApi toopherApi) + public UserTerminal(IDictionary response, ToopherApi toopherApi) { this.rawResponse = response; this.api = toopherApi; - try { + try + { this.id = (string)response["id"]; this.name = (string)response["name"]; this.requesterSpecifiedId = (string)response["requester_specified_id"]; - this.user = new User ((JsonObject)response["user"], toopherApi); - } catch (Exception ex) { - throw new RequestError ("Could not parse user terminal from response", ex); + this.user = new User((JsonObject)response["user"], toopherApi); + } catch (Exception ex) + { + throw new RequestError("Could not parse user terminal from response", ex); } } /// /// Update the user terminal object with response from the API. /// - public void RefreshFromServer () + public void RefreshFromServer() { - string endpoint = string.Format ("user_terminals/{0}", id); - var json = api.advanced.raw.get (endpoint); - Update (json); + string endpoint = string.Format("user_terminals/{0}", id); + var json = api.advanced.raw.get(endpoint); + Update(json); } /// /// Update the user object with provided response. /// /// The response from the API. - public void Update (IDictionary response) + public void Update(IDictionary response) { this.rawResponse = response; - try { + try + { this.name = (string)response["name"]; this.requesterSpecifiedId = (string)response["requester_specified_id"]; - this.user.Update ((JsonObject)response["user"]); - } catch (Exception ex) { - throw new RequestError ("Could not parse user terminal from response", ex); + this.user.Update((JsonObject)response["user"]); + } catch (Exception ex) + { + throw new RequestError("Could not parse user terminal from response", ex); } } } @@ -1103,23 +1187,25 @@ public string name private set; } - public override string ToString () + public override string ToString() { - return string.Format ("[Action: id={0}; name={1}]", id, name); + return string.Format("[Action: id={0}; name={1}]", id, name); } /// /// Provide information about the status of an action. /// /// The response from the API. - public Action (IDictionary response) + public Action(IDictionary response) { this.rawResponse = response; - try { + try + { this.id = (string)response["id"]; this.name = (string)response["name"]; - } catch (Exception ex) { - throw new RequestError ("Could not parse action from response", ex); + } catch (Exception ex) + { + throw new RequestError("Could not parse action from response", ex); } } @@ -1127,13 +1213,15 @@ public Action (IDictionary response) /// Update the user object with provided response. /// /// The response from the API. - public void Update (IDictionary response) + public void Update(IDictionary response) { this.rawResponse = response; - try { + try + { this.name = (string)response["name"]; - } catch (Exception ex) { - throw new RequestError ("Could not parse action from response", ex); + } catch (Exception ex) + { + throw new RequestError("Could not parse action from response", ex); } } } @@ -1141,9 +1229,9 @@ public void Update (IDictionary response) // An exception class used to indicate an error in a request public class RequestError : System.ApplicationException { - public RequestError () : base () { } - public RequestError (string message) : base (message) { } - public RequestError (string message, System.Exception inner) : base (message, inner) { } + public RequestError() : base() { } + public RequestError(string message) : base(message) { } + public RequestError(string message, System.Exception inner) : base(message, inner) { } } /// @@ -1187,7 +1275,7 @@ public class PairingDeactivatedError : RequestError public class WebClientProxy : IDisposable { - WebClient _theClient = new WebClient (); + WebClient _theClient = new WebClient(); public WebHeaderCollection Headers { get { return _theClient.Headers; } @@ -1198,19 +1286,20 @@ public NameValueCollection QueryString get { return _theClient.QueryString; } set { _theClient.QueryString = value; } } - virtual public byte[] UploadValues (string requestUri, string method, NameValueCollection parameters) + virtual public byte[] UploadValues(string requestUri, string method, NameValueCollection parameters) { - return _theClient.UploadValues (requestUri, method, parameters); + return _theClient.UploadValues(requestUri, method, parameters); } - virtual public string DownloadString (string requestUri) + virtual public string DownloadString(string requestUri) { - return _theClient.DownloadString (requestUri); + return _theClient.DownloadString(requestUri); } - public void Dispose () + public void Dispose() { - if (_theClient != null) { - _theClient.Dispose (); + if (_theClient != null) + { + _theClient.Dispose(); _theClient = null; } } @@ -1218,7 +1307,7 @@ public void Dispose () public interface IHttpWebResponse { - Stream GetResponseStream (); + Stream GetResponseStream(); HttpStatusCode StatusCode { get; } string StatusDescription { get; } } @@ -1226,23 +1315,25 @@ public interface IHttpWebResponse class HttpWebResponseWrapper : IHttpWebResponse { private HttpWebResponse _wrapped; - public HttpWebResponseWrapper (HttpWebResponse wrapped) + public HttpWebResponseWrapper(HttpWebResponse wrapped) { this._wrapped = wrapped; } - static public IHttpWebResponse create (object response) + static public IHttpWebResponse create(object response) { //if it already implements IHttpWebResponse, return it directory - if(typeof(IHttpWebResponse).IsAssignableFrom(response.GetType())){ + if(typeof(IHttpWebResponse).IsAssignableFrom(response.GetType())) + { return (IHttpWebResponse)response; - } else if (typeof(HttpWebResponse).IsAssignableFrom(response.GetType())) { + } else if (typeof(HttpWebResponse).IsAssignableFrom(response.GetType())) + { return new HttpWebResponseWrapper((HttpWebResponse)response); } else { throw new NotImplementedException("Don't know how to transmute " + response.GetType().ToString() + " into IHttpWebResponse"); } } - public Stream GetResponseStream () + public Stream GetResponseStream() { return _wrapped.GetResponseStream(); } diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 850bbc9..6a0662e 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -28,13 +28,13 @@ class MockWebResponse : WebResponse, IHttpWebResponse HttpStatusCode errorCode; Stream responseStream; - public MockWebResponse (HttpStatusCode errorCode, string responseBody) + public MockWebResponse(HttpStatusCode errorCode, string responseBody) { this.errorCode = errorCode; this.responseStream = new MemoryStream(System.Text.Encoding.UTF8.GetBytes(responseBody)); } - override public Stream GetResponseStream () + override public Stream GetResponseStream() { return responseStream; } @@ -49,10 +49,10 @@ public string StatusDescription get { return ""; } } } - public WebException makeError (HttpStatusCode errorCode, string responseBody) + public WebException makeError(HttpStatusCode errorCode, string responseBody) { - WebResponse response = new MockWebResponse (errorCode, responseBody); - return new WebException ("", null, WebExceptionStatus.ProtocolError, response); + WebResponse response = new MockWebResponse(errorCode, responseBody); + return new WebException("", null, WebExceptionStatus.ProtocolError, response); } public class WebClientMock : WebClientProxy { @@ -61,25 +61,29 @@ public class WebClientMock : WebClientProxy static public Exception ReturnException { get; set; } static public string ReturnValue { get; set; } static public string ReturnValueArray { get; set; } - string doit () + string doit() { - if (ReturnException != null) { + if (ReturnException != null) + { throw ReturnException; - } else if (ReturnValueArray != null) { + } else if (ReturnValueArray != null) + { var values = ReturnValueArray; ReturnValueArray = null; return values; - } else { + } else + { return ReturnValue; } } - override public byte[] UploadValues(string requestUri, string method, NameValueCollection parameters) { + override public byte[] UploadValues(string requestUri, string method, NameValueCollection parameters) + { LastRequestMethod = "POST"; LastRequestData = parameters; - return System.Text.Encoding.UTF8.GetBytes (doit()); + return System.Text.Encoding.UTF8.GetBytes(doit()); } - override public string DownloadString (string requestUri) + override public string DownloadString(string requestUri) { LastRequestMethod = "GET"; LastRequestData = this.QueryString; @@ -88,7 +92,7 @@ override public string DownloadString (string requestUri) } [SetUp] - public virtual void Init () + public virtual void Init() { WebClientMock.ReturnException = null; WebClientMock.ReturnValue = null; @@ -99,9 +103,9 @@ public virtual void Init () OAuthTools.SetDateOverride(null); } - public ToopherApi getToopherApi () + public ToopherApi getToopherApi() { - return new ToopherApi ("key", "secret", null, typeof (WebClientMock)); + return new ToopherApi("key", "secret", null, typeof(WebClientMock)); } } @@ -113,106 +117,108 @@ public class ToopherIframeTests : TestBase private DateTime TEST_DATE = new DateTime(1970, 1, 1, 0, 16, 40, 0); [SetUp] - public override void Init () + public override void Init() { ToopherIframe.SetDateOverride(null); OAuthTools.SetNonceOverride(null); OAuthTools.SetDateOverride(null); } - private ToopherIframe getToopherIframeApi () + private ToopherIframe getToopherIframeApi() { - return new ToopherIframe (TOOPHER_CONSUMER_KEY, TOOPHER_CONSUMER_SECRET, DEFAULT_BASE_URL); + return new ToopherIframe(TOOPHER_CONSUMER_KEY, TOOPHER_CONSUMER_SECRET, DEFAULT_BASE_URL); } [Test] - public void ToopherIframeVersionTest () + public void ToopherIframeVersionTest() { - Assert.IsTrue (int.Parse(ToopherIframe.IFRAME_VERSION) >= 1); + Assert.IsTrue(int.Parse(ToopherIframe.IFRAME_VERSION) >= 1); } [Test] - public void ToopherBaseUrlTest () + public void ToopherBaseUrlTest() { - StringAssert.Contains ("https", ToopherIframe.DEFAULT_BASE_URL); - Assert.IsTrue (System.Uri.IsWellFormedUriString (ToopherIframe.DEFAULT_BASE_URL, System.UriKind.Absolute)); + StringAssert.Contains("https", ToopherIframe.DEFAULT_BASE_URL); + Assert.IsTrue(System.Uri.IsWellFormedUriString(ToopherIframe.DEFAULT_BASE_URL, System.UriKind.Absolute)); } [Test] - public void GetAuthenticationUrlTest () + public void GetAuthenticationUrlTest() { var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - OAuthTools.SetNonceOverride (OAUTH_NONCE); - OAuthTools.SetDateOverride (TEST_DATE); + OAuthTools.SetNonceOverride(OAUTH_NONCE); + OAuthTools.SetDateOverride(TEST_DATE); string expected = "https://api.toopher.test/v1/web/authenticate?action_name=Log+In&expires=1300&requester_metadata=None&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=YN%2BkKNTaoypsB37fsjvMS8vsG5A%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; - var authUrl = api.GetAuthenticationUrl ("jdoe", "jdoe@example.com", REQUEST_TOKEN); - Assert.AreEqual (expected, authUrl); + var authUrl = api.GetAuthenticationUrl("jdoe", "jdoe@example.com", REQUEST_TOKEN); + Assert.AreEqual(expected, authUrl); } [Test] - public void GetAuthenticationUrlWithExtrasTest () + public void GetAuthenticationUrlWithExtrasTest() { Dictionary extras = new Dictionary(); extras.Add("allow_inline_pairing", "false"); var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - OAuthTools.SetNonceOverride (OAUTH_NONCE); - OAuthTools.SetDateOverride (TEST_DATE); + OAuthTools.SetNonceOverride(OAUTH_NONCE); + OAuthTools.SetDateOverride(TEST_DATE); string expected = "https://api.toopher.test/v1/web/authenticate?action_name=it+is+a+test&allow_inline_pairing=false&expires=1300&requester_metadata=None&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=W%2F2dcdsVc7YgdSCZuEo8ViHLlOo%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; - var authUrl = api.GetAuthenticationUrl ("jdoe", "jdoe@example.com", REQUEST_TOKEN, "it is a test", "None", extras); - Assert.AreEqual (expected, authUrl); + var authUrl = api.GetAuthenticationUrl("jdoe", "jdoe@example.com", REQUEST_TOKEN, "it is a test", "None", extras); + Assert.AreEqual(expected, authUrl); } [Test] - public void GetUserManagementUrlTest () + public void GetUserManagementUrlTest() { var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - OAuthTools.SetNonceOverride (OAUTH_NONCE); - OAuthTools.SetDateOverride (TEST_DATE); + OAuthTools.SetNonceOverride(OAUTH_NONCE); + OAuthTools.SetDateOverride(TEST_DATE); string expected = "https://api.toopher.test/v1/web/manage_user?expires=1300&reset_email=jdoe%40example.com&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=NjwH5yWPE2CCJL8v%2FMNknL%2BeTpE%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; - var userManagementUrl = api.GetUserManagementUrl ("jdoe", "jdoe@example.com"); - Assert.AreEqual (expected, userManagementUrl); + var userManagementUrl = api.GetUserManagementUrl("jdoe", "jdoe@example.com"); + Assert.AreEqual(expected, userManagementUrl); } [Test] - public void GetUserManagementUrlWithExtrasTest () + public void GetUserManagementUrlWithExtrasTest() { Dictionary extras = new Dictionary(); extras.Add("ttl", "100"); var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - OAuthTools.SetNonceOverride (OAUTH_NONCE); - OAuthTools.SetDateOverride (TEST_DATE); + OAuthTools.SetNonceOverride(OAUTH_NONCE); + OAuthTools.SetDateOverride(TEST_DATE); string expected = "https://api.toopher.test/v1/web/manage_user?expires=1100&reset_email=jdoe%40example.com&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=sV8qoKnxJ3fxfP6AHNa0eNFxzJs%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; - var userManagementUrl = api.GetUserManagementUrl ("jdoe", "jdoe@example.com", extras); - Assert.AreEqual (expected, userManagementUrl); + var userManagementUrl = api.GetUserManagementUrl("jdoe", "jdoe@example.com", extras); + Assert.AreEqual(expected, userManagementUrl); } [Test] - public void ValidatePostbackWithGoodSignatureIsSuccessfulTest () + public void ValidatePostbackWithGoodSignatureIsSuccessfulTest() { var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); + Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("session_token", new string[]{REQUEST_TOKEN}); data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - try { + try + { Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, 5)); - } catch (Exception) { + } catch (Exception) + { Assert.Fail("Valid signature, timestamp, and session token did not return validated data"); } } [Test] - public void ValidatePostbackWithBadSignatureFailsTest () + public void ValidatePostbackWithBadSignatureFailsTest() { var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); + Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("session_token", new string[]{REQUEST_TOKEN}); @@ -222,11 +228,11 @@ public void ValidatePostbackWithBadSignatureFailsTest () } [Test] - public void ValidatePostbackWithExpiredSignatureFailsTest () + public void ValidatePostbackWithExpiredSignatureFailsTest() { var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(new DateTime(1970, 2, 1, 0, 16, 40, 0)); - Dictionary data = new Dictionary (); + Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("session_token", new string[]{REQUEST_TOKEN}); @@ -236,11 +242,11 @@ public void ValidatePostbackWithExpiredSignatureFailsTest () } [Test] - public void ValidatePostbackWithInvalidSessionTokenFailsTest () + public void ValidatePostbackWithInvalidSessionTokenFailsTest() { var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); + Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("session_token", new string[]{"invalid token"}); @@ -250,11 +256,11 @@ public void ValidatePostbackWithInvalidSessionTokenFailsTest () } [Test] - public void ValidatePostbackMissingTimestampFailsTest () + public void ValidatePostbackMissingTimestampFailsTest() { var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); + Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); data.Add("session_token", new string[]{REQUEST_TOKEN}); data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); @@ -263,11 +269,11 @@ public void ValidatePostbackMissingTimestampFailsTest () } [Test] - public void ValidatePostbackMissingSignatureFailsTest () + public void ValidatePostbackMissingSignatureFailsTest() { var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); + Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("session_token", new string[]{REQUEST_TOKEN}); @@ -276,11 +282,11 @@ public void ValidatePostbackMissingSignatureFailsTest () } [Test] - public void ValidatePostbackMissingSessionTokenFailsTest () + public void ValidatePostbackMissingSessionTokenFailsTest() { var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary (); + Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); @@ -293,7 +299,7 @@ public void ValidatePostbackMissingSessionTokenFailsTest () public class ToopherApiTests : TestBase { [Test()] - public void ToopherVersionTest () + public void ToopherVersionTest() { string[] strs = ToopherApi.VERSION.Split('.'); int major = int.Parse(strs[0]); @@ -305,368 +311,368 @@ public void ToopherVersionTest () } [Test()] - public void ToopherBaseUrlTest () + public void ToopherBaseUrlTest() { - StringAssert.Contains ("https", ToopherApi.DEFAULT_BASE_URL); + StringAssert.Contains("https", ToopherApi.DEFAULT_BASE_URL); Assert.IsTrue(System.Uri.IsWellFormedUriString(ToopherApi.DEFAULT_BASE_URL, System.UriKind.Absolute)); } [Test] - public void CreatePairingWithPhraseTest () + public void CreatePairingWithPhraseTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; - Pairing pairing = api.Pair ("some user", "awkward turtle"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["pairing_phrase"], "awkward turtle"); - Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some user"); - Assert.AreEqual (pairing.id, "1"); + Pairing pairing = api.Pair("some user", "awkward turtle"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["pairing_phrase"], "awkward turtle"); + Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "some user"); + Assert.AreEqual(pairing.id, "1"); } [Test] - public void CreateSmsPairingTest () + public void CreateSmsPairingTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; - Pairing pairing = api.Pair ("some user", "555-555-5555"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["phone_number"], "555-555-5555"); - Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some user"); - Assert.AreEqual (pairing.id, "1"); + Pairing pairing = api.Pair("some user", "555-555-5555"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["phone_number"], "555-555-5555"); + Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "some user"); + Assert.AreEqual(pairing.id, "1"); } [Test] - public void CreateQrPairingTest () + public void CreateQrPairingTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; - Pairing pairing = api.Pair ("some user"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some user"); - Assert.AreEqual (pairing.id, "1"); + Pairing pairing = api.Pair("some user"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "some user"); + Assert.AreEqual(pairing.id, "1"); } [Test] - public void ArbitraryParametersOnPairTest () + public void ArbitraryParametersOnPairTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; - Pairing pairing = api.Pair ("awkward turtle", "some user", extras: new Dictionary(){{"test_param", "42"}}); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); - Assert.AreEqual (pairing.id, "1"); + Pairing pairing = api.Pair("awkward turtle", "some user", extras: new Dictionary(){{"test_param", "42"}}); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["test_param"], "42"); + Assert.AreEqual(pairing.id, "1"); } [Test] - public void AuthenticateWithPairingIdTest () + public void AuthenticateWithPairingIdTest() { - var api = getToopherApi (); + var api = getToopherApi(); string pairingId = Guid.NewGuid().ToString(); WebClientMock.ReturnValue = @"{""id"":""" + pairingId + @""", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"",""requester_specified_id"":""requesterSpecifiedId"",""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; AuthenticationRequest auth = api.Authenticate(pairingId, "test terminal"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["pairing_id"], pairingId); - Assert.AreEqual (WebClientMock.LastRequestData["terminal_name"], "test terminal"); - Assert.AreEqual (auth.id, pairingId); + Assert.AreEqual(WebClientMock.LastRequestData["pairing_id"], pairingId); + Assert.AreEqual(WebClientMock.LastRequestData["terminal_name"], "test terminal"); + Assert.AreEqual(auth.id, pairingId); } [Test] - public void AuthenticateWithUsernameAndExtrasTest () + public void AuthenticateWithUsernameAndExtrasTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; - AuthenticationRequest auth = api.Authenticate ("some other user", requesterSpecifiedId: "requester specified id", extras: new Dictionary() {{ "random_key" , "42" }}); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["user_name"], "some other user"); - Assert.AreEqual (WebClientMock.LastRequestData["requester_specified_terminal_id"], "requester specified id"); - Assert.AreEqual (WebClientMock.LastRequestData["random_key"], "42"); - Assert.AreEqual (auth.id, "1"); + AuthenticationRequest auth = api.Authenticate("some other user", requesterSpecifiedId: "requester specified id", extras: new Dictionary(){{ "random_key" , "42" }}); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "some other user"); + Assert.AreEqual(WebClientMock.LastRequestData["requester_specified_terminal_id"], "requester specified id"); + Assert.AreEqual(WebClientMock.LastRequestData["random_key"], "42"); + Assert.AreEqual(auth.id, "1"); } [Test] - public void ArbitraryParamtersOnAuthenticateTest () + public void ArbitraryParamtersOnAuthenticateTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; - AuthenticationRequest auth = api.Authenticate ("1", "test terminal", extras: new Dictionary () { { "test_param", "42" } }); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["test_param"], "42"); - Assert.AreEqual (auth.id, "1"); + AuthenticationRequest auth = api.Authenticate("1", "test terminal", extras: new Dictionary(){{ "test_param", "42" }}); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["test_param"], "42"); + Assert.AreEqual(auth.id, "1"); } [Test] - public void GenerateAdvancedApiUsageFactory () + public void GenerateAdvancedApiUsageFactory() { - var api = getToopherApi (); - Assert.IsInstanceOf (api.advanced); + var api = getToopherApi(); + Assert.IsInstanceOf(api.advanced); } [Test] - public void GenerateAdvancedPairings () + public void GenerateAdvancedPairings() { - var api = getToopherApi (); - Assert.IsInstanceOf (api.advanced.pairings); + var api = getToopherApi(); + Assert.IsInstanceOf(api.advanced.pairings); } [Test] - public void GenerateAdvancedRaw () + public void GenerateAdvancedRaw() { - var api = getToopherApi (); - Assert.IsInstanceOf (api.advanced.raw); + var api = getToopherApi(); + Assert.IsInstanceOf(api.advanced.raw); } [Test] - public void GenerateAdvancedAuthenticationRequests () + public void GenerateAdvancedAuthenticationRequests() { - var api = getToopherApi (); - Assert.IsInstanceOf (api.advanced.authenticationRequests); + var api = getToopherApi(); + Assert.IsInstanceOf(api.advanced.authenticationRequests); } [Test] - public void GenerateAdvancedUsers () + public void GenerateAdvancedUsers() { - var api = getToopherApi (); - Assert.IsInstanceOf (api.advanced.users); + var api = getToopherApi(); + Assert.IsInstanceOf(api.advanced.users); } [Test] - public void GenerateAdvancedUserTerminals () + public void GenerateAdvancedUserTerminals() { - var api = getToopherApi (); - Assert.IsInstanceOf (api.advanced.userTerminals); + var api = getToopherApi(); + Assert.IsInstanceOf(api.advanced.userTerminals); } [Test] - public void AdvancedPairingsGetByIdTest () + public void AdvancedPairingsGetByIdTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; - Pairing pairing = api.advanced.pairings.GetById ("1"); - Assert.IsInstanceOf (pairing); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (pairing.id, "1"); - Assert.AreEqual (pairing.user.name, "some user"); - Assert.AreEqual (pairing.user.id, "1"); - Assert.IsTrue (pairing.enabled); + Pairing pairing = api.advanced.pairings.GetById("1"); + Assert.IsInstanceOf(pairing); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(pairing.id, "1"); + Assert.AreEqual(pairing.user.name, "some user"); + Assert.AreEqual(pairing.user.id, "1"); + Assert.IsTrue(pairing.enabled); } [Test] - public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest () + public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""random_key"":""84""}"; - Pairing pairing = api.advanced.pairings.GetById ("1"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (pairing.id, "1"); - Assert.AreEqual (pairing.user.name, "some user"); - Assert.AreEqual (pairing.user.id, "1"); - Assert.IsTrue (pairing.enabled); - Assert.AreEqual (pairing["random_key"], "84"); + Pairing pairing = api.advanced.pairings.GetById("1"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(pairing.id, "1"); + Assert.AreEqual(pairing.user.name, "some user"); + Assert.AreEqual(pairing.user.id, "1"); + Assert.IsTrue(pairing.enabled); + Assert.AreEqual(pairing["random_key"], "84"); } [Test] - public void AdvancedAuthenticationRequestsGetByIdTest () + public void AdvancedAuthenticationRequestsGetByIdTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; - AuthenticationRequest auth = api.advanced.authenticationRequests.GetById ("1"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (auth.id, "1"); - Assert.IsFalse (auth.pending); - Assert.IsTrue (auth.granted); - Assert.AreEqual (auth.reason, "its a test"); - Assert.AreEqual (auth.terminal.id, "1"); - Assert.AreEqual (auth.terminal.name, "test terminal"); + AuthenticationRequest auth = api.advanced.authenticationRequests.GetById("1"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(auth.id, "1"); + Assert.IsFalse(auth.pending); + Assert.IsTrue(auth.granted); + Assert.AreEqual(auth.reason, "its a test"); + Assert.AreEqual(auth.terminal.id, "1"); + Assert.AreEqual(auth.terminal.name, "test terminal"); } [Test] - public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest () + public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}, ""random_key"":""84""}"; - AuthenticationRequest auth = api.advanced.authenticationRequests.GetById ("1"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (auth.id, "1"); - Assert.IsFalse (auth.pending); - Assert.IsTrue (auth.granted); - Assert.AreEqual (auth.reason, "its a test"); - Assert.AreEqual (auth.terminal.id, "1"); - Assert.AreEqual (auth.terminal.name, "test terminal"); - Assert.AreEqual (auth["random_key"], "84"); + AuthenticationRequest auth = api.advanced.authenticationRequests.GetById("1"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(auth.id, "1"); + Assert.IsFalse(auth.pending); + Assert.IsTrue(auth.granted); + Assert.AreEqual(auth.reason, "its a test"); + Assert.AreEqual(auth.terminal.id, "1"); + Assert.AreEqual(auth.terminal.name, "test terminal"); + Assert.AreEqual(auth["random_key"], "84"); } [Test] - public void AdvancedUsersGetByIdTest () + public void AdvancedUsersGetByIdTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = USER_RESPONSE; - User user = api.advanced.users.GetById ("1"); - Assert.IsInstanceOf (user); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "userName"); - Assert.IsTrue (user.toopherAuthenticationEnabled); + User user = api.advanced.users.GetById("1"); + Assert.IsInstanceOf(user); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(user.id, "1"); + Assert.AreEqual(user.name, "userName"); + Assert.IsTrue(user.toopherAuthenticationEnabled); } [Test] - public void AccessArbitraryKeysInAdvancedUsersGetByIdTest () + public void AccessArbitraryKeysInAdvancedUsersGetByIdTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true, ""random_key"":""84""}"; - User user = api.advanced.users.GetById ("1"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "userName"); - Assert.IsTrue (user.toopherAuthenticationEnabled); - Assert.AreEqual (user["random_key"], "84"); + User user = api.advanced.users.GetById("1"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(user.id, "1"); + Assert.AreEqual(user.name, "userName"); + Assert.IsTrue(user.toopherAuthenticationEnabled); + Assert.AreEqual(user["random_key"], "84"); } [Test] - public void AdvancedUsersGetByNameTest () + public void AdvancedUsersGetByNameTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValueArray = @"[{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}]"; WebClientMock.ReturnValue = USER_RESPONSE; User user = api.advanced.users.GetByName("userName"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "userName"); - Assert.IsTrue (user.toopherAuthenticationEnabled); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(user.id, "1"); + Assert.AreEqual(user.name, "userName"); + Assert.IsTrue(user.toopherAuthenticationEnabled); } [Test] - public void AdvancedUsersCreateTest () + public void AdvancedUsersCreateTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = USER_RESPONSE; - User user = api.advanced.users.Create ("userName"); - Assert.IsInstanceOf (user); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "userName"); - Assert.IsTrue (user.toopherAuthenticationEnabled); + User user = api.advanced.users.Create("userName"); + Assert.IsInstanceOf(user); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(user.id, "1"); + Assert.AreEqual(user.name, "userName"); + Assert.IsTrue(user.toopherAuthenticationEnabled); } [Test] - public void AdvancedUsersCreateWithParamsTest () + public void AdvancedUsersCreateWithParamsTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = USER_RESPONSE; NameValueCollection parameters = new NameValueCollection(); - parameters.Add ("foo", "bar"); - User user = api.advanced.users.Create ("userName", parameters); - Assert.IsInstanceOf (user); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["foo"], "bar"); - Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "userName"); - Assert.IsTrue (user.toopherAuthenticationEnabled); + parameters.Add("foo", "bar"); + User user = api.advanced.users.Create("userName", parameters); + Assert.IsInstanceOf(user); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["foo"], "bar"); + Assert.AreEqual(user.id, "1"); + Assert.AreEqual(user.name, "userName"); + Assert.IsTrue(user.toopherAuthenticationEnabled); } [Test] - public void AdvancedUserTerminalsGetByIdTest () + public void AdvancedUserTerminalsGetByIdTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; - UserTerminal userTerminal = api.advanced.userTerminals.GetById ("1"); - Assert.IsInstanceOf (userTerminal); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (userTerminal.id, "1"); - Assert.AreEqual (userTerminal.name, "userTerminalName"); - Assert.AreEqual (userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); - Assert.IsInstanceOf (userTerminal.user); + UserTerminal userTerminal = api.advanced.userTerminals.GetById("1"); + Assert.IsInstanceOf(userTerminal); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(userTerminal.id, "1"); + Assert.AreEqual(userTerminal.name, "userTerminalName"); + Assert.AreEqual(userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); + Assert.IsInstanceOf(userTerminal.user); } [Test] - public void AccessArbitraryKeysInAdvancedUserTerminalsGetByIdTest () + public void AccessArbitraryKeysInAdvancedUserTerminalsGetByIdTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"",""random_key"":""84"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; - UserTerminal userTerminal = api.advanced.userTerminals.GetById ("1"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (userTerminal.id, "1"); - Assert.AreEqual (userTerminal.name, "userTerminalName"); - Assert.IsTrue (userTerminal.user.toopherAuthenticationEnabled); - Assert.AreEqual (userTerminal["random_key"], "84"); + UserTerminal userTerminal = api.advanced.userTerminals.GetById("1"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(userTerminal.id, "1"); + Assert.AreEqual(userTerminal.name, "userTerminalName"); + Assert.IsTrue(userTerminal.user.toopherAuthenticationEnabled); + Assert.AreEqual(userTerminal["random_key"], "84"); } [Test] - public void AdvancedUserTerminalsCreateTest () + public void AdvancedUserTerminalsCreateTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; - UserTerminal userTerminal = api.advanced.userTerminals.Create ("userName", "userTerminalName", "requesterSpecifiedId"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (userTerminal.id, "1"); - Assert.AreEqual (userTerminal.name, "userTerminalName"); - Assert.AreEqual (userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); - Assert.IsInstanceOf (userTerminal.user); + UserTerminal userTerminal = api.advanced.userTerminals.Create("userName", "userTerminalName", "requesterSpecifiedId"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(userTerminal.id, "1"); + Assert.AreEqual(userTerminal.name, "userTerminalName"); + Assert.AreEqual(userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); + Assert.IsInstanceOf(userTerminal.user); } [Test] - public void AdvancedUserTerminalsCreateWithParamsTest () + public void AdvancedUserTerminalsCreateWithParamsTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; NameValueCollection parameters = new NameValueCollection(); - parameters.Add ("foo", "bar"); - UserTerminal userTerminal = api.advanced.userTerminals.Create ("userName", "userTerminalName", "requesterSpecifiedId", parameters); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["foo"], "bar"); - Assert.AreEqual (userTerminal.id, "1"); - Assert.AreEqual (userTerminal.name, "userTerminalName"); - Assert.AreEqual (userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); - Assert.IsInstanceOf (userTerminal.user); + parameters.Add("foo", "bar"); + UserTerminal userTerminal = api.advanced.userTerminals.Create("userName", "userTerminalName", "requesterSpecifiedId", parameters); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["foo"], "bar"); + Assert.AreEqual(userTerminal.id, "1"); + Assert.AreEqual(userTerminal.name, "userTerminalName"); + Assert.AreEqual(userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); + Assert.IsInstanceOf(userTerminal.user); } [Test] [ExpectedException(typeof(UserDisabledError))] - public void DisabledUserRaisesCorrectErrorTest () + public void DisabledUserRaisesCorrectErrorTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":704, ""error_message"":""The specified user has disabled Toopher authentication.""}"); - api.Authenticate ("some disabled user", requesterSpecifiedId: "some random string"); + api.Authenticate("some disabled user", requesterSpecifiedId: "some random string"); } [Test] - [ExpectedException (typeof (UserUnknownError))] - public void UnknownUserRaisesCorrectErrorTest () + [ExpectedException(typeof(UserUnknownError))] + public void UnknownUserRaisesCorrectErrorTest() { - var api = getToopherApi (); - WebClientMock.ReturnException = makeError ((HttpStatusCode)409, + var api = getToopherApi(); + WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":705, ""error_message"":""No matching user exists.""}"); - api.Authenticate ("some unknown user", requesterSpecifiedId: "some random string"); + api.Authenticate("some unknown user", requesterSpecifiedId: "some random string"); } [Test] - [ExpectedException (typeof (TerminalUnknownError))] - public void UnknownTerminalRaisesCorrectErrorTest () + [ExpectedException(typeof(TerminalUnknownError))] + public void UnknownTerminalRaisesCorrectErrorTest() { - var api = getToopherApi (); - WebClientMock.ReturnException = makeError ((HttpStatusCode)409, + var api = getToopherApi(); + WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":706, ""error_message"":""No matching terminal exists.""}"); - api.Authenticate ("some unknown user", requesterSpecifiedId: "some random string"); + api.Authenticate("some unknown user", requesterSpecifiedId: "some random string"); } [Test] - [ExpectedException (typeof (PairingDeactivatedError))] - public void DeactivatedPairingRaisesCorrectErrorTest () + [ExpectedException(typeof(PairingDeactivatedError))] + public void DeactivatedPairingRaisesCorrectErrorTest() { - var api = getToopherApi (); - WebClientMock.ReturnException = makeError ((HttpStatusCode)409, + var api = getToopherApi(); + WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":601, ""error_message"":""This pairing has been deactivated.""}"); - api.Authenticate ("some disabled user", requesterSpecifiedId: "some random string"); + api.Authenticate("some disabled user", requesterSpecifiedId: "some random string"); } [Test] - [ExpectedException (typeof (PairingDeactivatedError))] - public void UnauthorizedPairingRaisesCorrectErrorTest () + [ExpectedException(typeof(PairingDeactivatedError))] + public void UnauthorizedPairingRaisesCorrectErrorTest() { - var api = getToopherApi (); - WebClientMock.ReturnException = makeError ((HttpStatusCode)409, + var api = getToopherApi(); + WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":601, ""error_message"":""This pairing has not been authorized to authenticate.""}"); - api.Authenticate ("some unauthorized user", requesterSpecifiedId: "some random string"); + api.Authenticate("some unauthorized user", requesterSpecifiedId: "some random string"); } } @@ -676,64 +682,64 @@ public class PairingTests : TestBase private IDictionary PAIRING_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""userName"", ""toopher_authentication_enabled"":true}}"); [Test] - public void CreatePairingTest () + public void CreatePairingTest() { - var api = getToopherApi (); - Pairing pairing = new Pairing (PAIRING_DICT, api); - Assert.AreEqual (pairing.id, "1"); - Assert.IsFalse (pairing.pending); - Assert.IsTrue (pairing.enabled); - Assert.AreEqual (pairing.user.id, "1"); - Assert.AreEqual (pairing.user.name, "userName"); - Assert.IsTrue (pairing.user.toopherAuthenticationEnabled); + var api = getToopherApi(); + Pairing pairing = new Pairing(PAIRING_DICT, api); + Assert.AreEqual(pairing.id, "1"); + Assert.IsFalse(pairing.pending); + Assert.IsTrue(pairing.enabled); + Assert.AreEqual(pairing.user.id, "1"); + Assert.AreEqual(pairing.user.name, "userName"); + Assert.IsTrue(pairing.user.toopherAuthenticationEnabled); } [Test] - public void PairingRefreshFromServerTest () + public void PairingRefreshFromServerTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""enabled"":false, ""user"":{""id"":""1"",""name"":""userNameChanged"", ""toopher_authentication_enabled"":true}}"; - Pairing pairing = new Pairing (PAIRING_DICT, api); - pairing.RefreshFromServer (); - Assert.IsInstanceOf (pairing); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (pairing.id, "1"); - Assert.AreEqual (pairing.user.name, "userNameChanged"); - Assert.IsFalse (pairing.enabled); - Assert.IsTrue (pairing.pending); + Pairing pairing = new Pairing(PAIRING_DICT, api); + pairing.RefreshFromServer(); + Assert.IsInstanceOf(pairing); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(pairing.id, "1"); + Assert.AreEqual(pairing.user.name, "userNameChanged"); + Assert.IsFalse(pairing.enabled); + Assert.IsTrue(pairing.pending); } [Test] - public void PairingGetResetLinkTest () + public void PairingGetResetLinkTest() { - var api = getToopherApi (); + var api = getToopherApi(); string link = "http://api.toopher.test/v1/pairings/1/reset?reset_authorization=abcde"; WebClientMock.ReturnValue = @"{""url"":""" + link + @"""}"; - Pairing pairing = new Pairing (PAIRING_DICT, api); - string returnedLink = pairing.GetResetLink (); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (returnedLink, link); + Pairing pairing = new Pairing(PAIRING_DICT, api); + string returnedLink = pairing.GetResetLink(); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(returnedLink, link); } [Test] - public void PairingEmailResetLinkTest () + public void PairingEmailResetLinkTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = @"{}"; - Pairing pairing = new Pairing (PAIRING_DICT, api); - pairing.EmailResetLink ("test@test.com"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["reset_email"], "test@test.com"); + Pairing pairing = new Pairing(PAIRING_DICT, api); + pairing.EmailResetLink("test@test.com"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["reset_email"], "test@test.com"); } [Test] - public void PairingGetQrCodeImage () + public void PairingGetQrCodeImage() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = @"{}"; - Pairing pairing = new Pairing (PAIRING_DICT, api); - pairing.GetQrCodeImage (); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); + Pairing pairing = new Pairing(PAIRING_DICT, api); + pairing.GetQrCodeImage(); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); } } @@ -744,53 +750,53 @@ public class AuthenticationRequestTests : TestBase private IDictionary AUTH_REQUEST_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""pending"":true, ""granted"":false, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"); [Test] - public void CreateAuthenticationRequestTest () + public void CreateAuthenticationRequestTest() { - var api = getToopherApi (); - AuthenticationRequest auth = new AuthenticationRequest (AUTH_REQUEST_DICT, api); - Assert.AreEqual (auth.id, "1"); - Assert.IsTrue (auth.pending); - Assert.IsFalse (auth.granted); - Assert.IsFalse (auth.automated); - Assert.AreEqual (auth.reasonCode, 1); - Assert.AreEqual (auth.reason, "its a test"); - Assert.AreEqual (auth.terminal.id, "1"); - Assert.AreEqual (auth.terminal.name, "test terminal"); - Assert.AreEqual (auth.terminal.requesterSpecifiedId, "requesterSpecifiedId"); - Assert.AreEqual (auth.user.id, "1"); - Assert.AreEqual (auth.user.name, "some user"); - Assert.IsTrue (auth.user.toopherAuthenticationEnabled); - Assert.AreEqual (auth.action.id, "1"); - Assert.AreEqual (auth.action.name, "actionName"); + var api = getToopherApi(); + AuthenticationRequest auth = new AuthenticationRequest(AUTH_REQUEST_DICT, api); + Assert.AreEqual(auth.id, "1"); + Assert.IsTrue(auth.pending); + Assert.IsFalse(auth.granted); + Assert.IsFalse(auth.automated); + Assert.AreEqual(auth.reasonCode, 1); + Assert.AreEqual(auth.reason, "its a test"); + Assert.AreEqual(auth.terminal.id, "1"); + Assert.AreEqual(auth.terminal.name, "test terminal"); + Assert.AreEqual(auth.terminal.requesterSpecifiedId, "requesterSpecifiedId"); + Assert.AreEqual(auth.user.id, "1"); + Assert.AreEqual(auth.user.name, "some user"); + Assert.IsTrue(auth.user.toopherAuthenticationEnabled); + Assert.AreEqual(auth.action.id, "1"); + Assert.AreEqual(auth.action.name, "actionName"); } [Test] - public void AuthenticationRequestRefreshFromServerTest () + public void AuthenticationRequestRefreshFromServerTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""granted"":true, ""automated"":false, ""reason_code"":2, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal CHANGED"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user CHANGED"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName CHANGED""}}"; - AuthenticationRequest auth = new AuthenticationRequest (AUTH_REQUEST_DICT, api); - auth.RefreshFromServer (); - Assert.IsInstanceOf (auth); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.IsTrue (auth.pending); - Assert.AreEqual (auth.reasonCode, 2); - Assert.AreEqual (auth.terminal.name, "test terminal CHANGED"); - Assert.AreEqual (auth.user.name, "some user CHANGED"); - Assert.AreEqual (auth.action.name, "actionName CHANGED"); + AuthenticationRequest auth = new AuthenticationRequest(AUTH_REQUEST_DICT, api); + auth.RefreshFromServer(); + Assert.IsInstanceOf(auth); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.IsTrue(auth.pending); + Assert.AreEqual(auth.reasonCode, 2); + Assert.AreEqual(auth.terminal.name, "test terminal CHANGED"); + Assert.AreEqual(auth.user.name, "some user CHANGED"); + Assert.AreEqual(auth.action.name, "actionName CHANGED"); } [Test] - public void AuthenticationRequestGrantWithOtpTest () + public void AuthenticationRequestGrantWithOtpTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; - AuthenticationRequest auth = new AuthenticationRequest (AUTH_REQUEST_DICT, api); - auth.GrantWithOtp ("123456"); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["otp"], "123456"); - Assert.AreEqual (auth.id, "1"); - Assert.IsTrue (auth.granted); + AuthenticationRequest auth = new AuthenticationRequest(AUTH_REQUEST_DICT, api); + auth.GrantWithOtp("123456"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["otp"], "123456"); + Assert.AreEqual(auth.id, "1"); + Assert.IsTrue(auth.granted); } } @@ -801,64 +807,64 @@ public class UserTests : TestBase private IDictionary USER_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}"); [Test] - public void CreateUserTest () + public void CreateUserTest() { - var api = getToopherApi (); - Toopher.User user = new Toopher.User (USER_DICT, api); - Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "userName"); - Assert.IsTrue (user.toopherAuthenticationEnabled); + var api = getToopherApi(); + Toopher.User user = new Toopher.User(USER_DICT, api); + Assert.AreEqual(user.id, "1"); + Assert.AreEqual(user.name, "userName"); + Assert.IsTrue(user.toopherAuthenticationEnabled); } [Test] - public void UserRefreshFromServerTest () + public void UserRefreshFromServerTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userNameChanged"", ""toopher_authentication_enabled"":false}"; - User user = new User (USER_DICT, api); - user.RefreshFromServer (); - Assert.IsInstanceOf (user); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (user.id, "1"); - Assert.AreEqual (user.name, "userNameChanged"); - Assert.IsFalse (user.toopherAuthenticationEnabled); + User user = new User(USER_DICT, api); + user.RefreshFromServer(); + Assert.IsInstanceOf(user); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(user.id, "1"); + Assert.AreEqual(user.name, "userNameChanged"); + Assert.IsFalse(user.toopherAuthenticationEnabled); } [Test] - public void UserEnableToopherAuthentication () + public void UserEnableToopherAuthentication() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = USER_RESPONSE; - User user = new User (USER_DICT, api); - user.EnableToopherAuthentication (); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["toopher_authentication_enabled"], "true"); - Assert.AreEqual (user.id, "1"); - Assert.IsTrue (user.toopherAuthenticationEnabled); + User user = new User(USER_DICT, api); + user.EnableToopherAuthentication(); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["toopher_authentication_enabled"], "true"); + Assert.AreEqual(user.id, "1"); + Assert.IsTrue(user.toopherAuthenticationEnabled); } [Test] - public void UserDisableToopherAuthentication () + public void UserDisableToopherAuthentication() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":false}"; - User user = new User (USER_DICT, api); - user.DisableToopherAuthentication (); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["toopher_authentication_enabled"], "false"); - Assert.AreEqual (user.id, "1"); - Assert.IsFalse (user.toopherAuthenticationEnabled); + User user = new User(USER_DICT, api); + user.DisableToopherAuthentication(); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["toopher_authentication_enabled"], "false"); + Assert.AreEqual(user.id, "1"); + Assert.IsFalse(user.toopherAuthenticationEnabled); } [Test] - public void UserResetTest () + public void UserResetTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = USER_RESPONSE; - User user = new User (USER_DICT, api); - user.Reset (); - Assert.AreEqual (WebClientMock.LastRequestMethod, "POST"); - Assert.AreEqual (WebClientMock.LastRequestData["name"], "userName"); + User user = new User(USER_DICT, api); + user.Reset(); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["name"], "userName"); } } @@ -869,31 +875,31 @@ public class UserTerminalTests : TestBase private IDictionary USER_TERMINAL_DICT = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"); [Test] - public void CreateUserTerminalTest () + public void CreateUserTerminalTest() { - var api = getToopherApi (); - UserTerminal userTerminal = new UserTerminal (USER_TERMINAL_DICT, api); - Assert.AreEqual (userTerminal.id, "1"); - Assert.AreEqual (userTerminal.name, "userTerminalName"); - Assert.AreEqual (userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); - Assert.AreEqual (userTerminal.user.id, "1"); - Assert.AreEqual (userTerminal.user.name, "userName"); - Assert.IsTrue (userTerminal.user.toopherAuthenticationEnabled); + var api = getToopherApi(); + UserTerminal userTerminal = new UserTerminal(USER_TERMINAL_DICT, api); + Assert.AreEqual(userTerminal.id, "1"); + Assert.AreEqual(userTerminal.name, "userTerminalName"); + Assert.AreEqual(userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); + Assert.AreEqual(userTerminal.user.id, "1"); + Assert.AreEqual(userTerminal.user.name, "userName"); + Assert.IsTrue(userTerminal.user.toopherAuthenticationEnabled); } [Test] - public void UserTerminalRefreshFromServerTest () + public void UserTerminalRefreshFromServerTest() { - var api = getToopherApi (); + var api = getToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalNameChanged"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userNameChanged"", ""toopher_authentication_enabled"":false}}"; - UserTerminal userTerminal = new UserTerminal (USER_TERMINAL_DICT, api); - userTerminal.RefreshFromServer (); - Assert.IsInstanceOf (userTerminal); - Assert.AreEqual (WebClientMock.LastRequestMethod, "GET"); - Assert.AreEqual (userTerminal.id, "1"); - Assert.AreEqual (userTerminal.name, "userTerminalNameChanged"); - Assert.AreEqual (userTerminal.user.name, "userNameChanged"); - Assert.IsFalse (userTerminal.user.toopherAuthenticationEnabled); + UserTerminal userTerminal = new UserTerminal(USER_TERMINAL_DICT, api); + userTerminal.RefreshFromServer(); + Assert.IsInstanceOf(userTerminal); + Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(userTerminal.id, "1"); + Assert.AreEqual(userTerminal.name, "userTerminalNameChanged"); + Assert.AreEqual(userTerminal.user.name, "userNameChanged"); + Assert.IsFalse(userTerminal.user.toopherAuthenticationEnabled); } } @@ -901,12 +907,12 @@ public void UserTerminalRefreshFromServerTest () public class ActionTests : TestBase { [Test] - public void CreateActionTest () + public void CreateActionTest() { var response = (IDictionary)SimpleJson.SimpleJson.DeserializeObject(@"{""id"":""1"", ""name"":""actionName""}"); - Toopher.Action action = new Toopher.Action (response); - Assert.AreEqual (action.id, "1"); - Assert.AreEqual (action.name, "actionName"); + Toopher.Action action = new Toopher.Action(response); + Assert.AreEqual(action.id, "1"); + Assert.AreEqual(action.name, "actionName"); } } } From 0da3bd05142d6b056bc800141f8c6c7258f0e1f1 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Feb 2015 16:19:43 -0600 Subject: [PATCH 078/103] Remove unnecessary line from demo --- ToopherDotNetDemo/ToopherDotNetDemo.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/ToopherDotNetDemo/ToopherDotNetDemo.cs b/ToopherDotNetDemo/ToopherDotNetDemo.cs index 4663951..c65d886 100644 --- a/ToopherDotNetDemo/ToopherDotNetDemo.cs +++ b/ToopherDotNetDemo/ToopherDotNetDemo.cs @@ -127,7 +127,6 @@ public static void Main (string[] args) string automation = authRequest.automated ? "automatically " : ""; string result = authRequest.granted ? "granted" : "denied"; Console.WriteLine ("The request was " + automation + result + "!"); - Console.WriteLine ("This request " + ((bool)authRequest["totp_valid"] ? "had" : "DID NOT HAVE") + " a valid authenticator OTP."); break; } } From 5a58c3b7df45b7c46fc677e27f0613ca4ac3ca12 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Feb 2015 16:24:17 -0600 Subject: [PATCH 079/103] Remove unnecessary comment --- ToopherDotNet/ToopherDotNet.cs | 1 - 1 file changed, 1 deletion(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index b2d6413..d587d15 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -639,7 +639,6 @@ private object request(string method, string endpoint, NameValueCollection param { try { - // Attempt to parse JSON response var json = (JsonObject)SimpleJson.SimpleJson.DeserializeObject(error_message); parseRequestError(json); } catch (RequestError e) From 37ba1c5e4c948c2a09259bb3625f39e56e935caa Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Feb 2015 16:44:43 -0600 Subject: [PATCH 080/103] Refactor DateOverride to not use nullable type --- ToopherDotNet/OAuth/OAuthTools.cs | 10 +++++++--- ToopherDotNet/ToopherDotNet.cs | 6 +++--- ToopherDotNetTests/ToopherDotNetTests.cs | 11 ----------- 3 files changed, 10 insertions(+), 17 deletions(-) diff --git a/ToopherDotNet/OAuth/OAuthTools.cs b/ToopherDotNet/OAuth/OAuthTools.cs index 0352e5c..53c5c0d 100644 --- a/ToopherDotNet/OAuth/OAuthTools.cs +++ b/ToopherDotNet/OAuth/OAuthTools.cs @@ -27,9 +27,9 @@ public static void SetNonceOverride (string nonceOverride) OAuthTools.nonceOverride = nonceOverride; } - private static DateTime? dateOverride; + private static DateTime dateOverride; - public static void SetDateOverride (DateTime? dateOverride) + public static void SetDateOverride (DateTime dateOverride) { OAuthTools.dateOverride = dateOverride; } @@ -87,7 +87,11 @@ public static string GetNonce() /// public static string GetTimestamp() { - return GetTimestamp(dateOverride ?? DateTime.UtcNow); + if (dateOverride == default(DateTime)) { + return GetTimestamp(DateTime.UtcNow); + } else { + return GetTimestamp(dateOverride); + } } /// diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index d587d15..7ca404e 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -27,16 +27,16 @@ public class ToopherIframe private string baseUrl; private string consumerKey; private string consumerSecret; - private static DateTime? dateOverride; + private static DateTime dateOverride; - public static void SetDateOverride(DateTime? dateOverride) + public static void SetDateOverride(DateTime dateOverride) { ToopherIframe.dateOverride = dateOverride; } private static DateTime GetDate() { - return dateOverride ?? DateTime.UtcNow; + return dateOverride == default(DateTime) ? DateTime.UtcNow : dateOverride; } /// diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 6a0662e..04f4b26 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -98,9 +98,6 @@ public virtual void Init() WebClientMock.ReturnValue = null; WebClientMock.LastRequestMethod = null; WebClientMock.LastRequestData = null; - ToopherIframe.SetDateOverride(null); - OAuthTools.SetNonceOverride(null); - OAuthTools.SetDateOverride(null); } public ToopherApi getToopherApi() @@ -116,14 +113,6 @@ public class ToopherIframeTests : TestBase private const string OAUTH_NONCE = "12345678"; private DateTime TEST_DATE = new DateTime(1970, 1, 1, 0, 16, 40, 0); - [SetUp] - public override void Init() - { - ToopherIframe.SetDateOverride(null); - OAuthTools.SetNonceOverride(null); - OAuthTools.SetDateOverride(null); - } - private ToopherIframe getToopherIframeApi() { return new ToopherIframe(TOOPHER_CONSUMER_KEY, TOOPHER_CONSUMER_SECRET, DEFAULT_BASE_URL); From 386ccfda712e70a52160eacddd0ddeae542b8d9a Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Feb 2015 17:02:07 -0600 Subject: [PATCH 081/103] Cleanup spacing --- ToopherDotNet/ToopherDotNet.cs | 28 +++++++--------------------- 1 file changed, 7 insertions(+), 21 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 7ca404e..4457aae 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -714,10 +714,7 @@ public class Pairing public object this[string key] { - get - { - return rawResponse[key]; - } + get { return rawResponse[key]; } } public string id @@ -846,10 +843,7 @@ public class AuthenticationRequest public object this[string key] { - get - { - return rawResponse[key]; - } + get { return rawResponse[key]; } } public string id @@ -967,10 +961,7 @@ public class User public object this[string key] { - get - { - return rawResponse[key]; - } + get { return rawResponse[key]; } } public string id @@ -1086,10 +1077,7 @@ public class UserTerminal public object this[string key] { - get - { - return rawResponse[key]; - } + get { return rawResponse[key]; } } public string id @@ -1169,10 +1157,7 @@ public class Action private IDictionary rawResponse; public object this[string key] { - get - { - return rawResponse[key]; - } + get { return rawResponse[key]; } } public string id @@ -1327,7 +1312,8 @@ static public IHttpWebResponse create(object response) } else if (typeof(HttpWebResponse).IsAssignableFrom(response.GetType())) { return new HttpWebResponseWrapper((HttpWebResponse)response); - } else { + } else + { throw new NotImplementedException("Don't know how to transmute " + response.GetType().ToString() + " into IHttpWebResponse"); } } From 0a15895d28e87171e5bceed446b10b3cc0025c43 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Feb 2015 17:03:13 -0600 Subject: [PATCH 082/103] Reorder args for Pair test and add assertions --- ToopherDotNetTests/ToopherDotNetTests.cs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 04f4b26..6d2a6f6 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -346,9 +346,11 @@ public void ArbitraryParametersOnPairTest() { var api = getToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; - Pairing pairing = api.Pair("awkward turtle", "some user", extras: new Dictionary(){{"test_param", "42"}}); + Pairing pairing = api.Pair("some user", "awkward turtle", extras: new Dictionary(){{"test_param", "42"}}); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["test_param"], "42"); + Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "some user"); + Assert.AreEqual(WebClientMock.LastRequestData["pairing_phrase"], "awkward turtle"); Assert.AreEqual(pairing.id, "1"); } From 3cd4fee24d24681d066359254c2d0e4dbf26dc44 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 26 Feb 2015 11:05:17 -0600 Subject: [PATCH 083/103] Add overload for validatePostback --- ToopherDotNet/ToopherDotNet.cs | 44 +++++++++++++----------- ToopherDotNetTests/ToopherDotNetTests.cs | 21 +++++++++++ 2 files changed, 45 insertions(+), 20 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 4457aae..d61cf63 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -134,37 +134,41 @@ public string GetUserManagementUrl(string userName, string resetEmail, Dictionar return GetOauthUrl(baseUrl + "web/manage_user", parameters); } + public Dictionary ValidatePostback(Dictionary parameters, string requestToken, long ttl = DEFAULT_TTL) + { + Dictionary data = new Dictionary(); + foreach (var entry in parameters) + { + if (entry.Value.Length > 0) + { + data.Add(entry.Key, entry.Value[0]); + } + } + return ValidatePostback(data, requestToken, ttl); + } + /// /// Verify the authenticity of data returned from the Toopher iFrame by validating the crytographic signature. /// /// The data returned from the iFrame. - /// The session token + /// The request token /// Time-To-Live (seconds) to enforce on the Toopher API signature. This value sets the maximum duration between the Toopher API creating the signature and the signature being validated on your server. /// A Dictionary of the validated data if the signature is valid, or null if the signature is invalid. - public Dictionary ValidatePostback(Dictionary parameters, string sessionToken, long ttl) + public Dictionary ValidatePostback(Dictionary parameters, string requestToken, long ttl = DEFAULT_TTL) { try { List missingKeys = new List(); - Dictionary data = new Dictionary(); - - foreach (var entry in parameters) - { - if (entry.Value.Length > 0) - { - data.Add(entry.Key, entry.Value[0]); - } - } - if (!data.ContainsKey("toopher_sig")) + if (!parameters.ContainsKey("toopher_sig")) { missingKeys.Add("toopher_sig"); } - if (!data.ContainsKey("timestamp")) + if (!parameters.ContainsKey("timestamp")) { missingKeys.Add("timestamp"); } - if (!data.ContainsKey("session_token")) + if (!parameters.ContainsKey("session_token")) { missingKeys.Add("session_token"); } @@ -174,17 +178,17 @@ public Dictionary ValidatePostback(Dictionary throw new SignatureValidationError("Missing required keys: " + keys); } - if (data["session_token"] != sessionToken) + if (parameters["session_token"] != requestToken) { throw new SignatureValidationError("Session token does not match expected value"); } - string maybeSignature = data["toopher_sig"]; - data.Remove("toopher_sig"); + string maybeSignature = parameters["toopher_sig"]; + parameters.Remove("toopher_sig"); var signatureValid = false; try { - var computedSig = Signature(consumerSecret, data); + var computedSig = Signature(consumerSecret, parameters); signatureValid = computedSig == maybeSignature; } catch (Exception e) { @@ -196,13 +200,13 @@ public Dictionary ValidatePostback(Dictionary throw new SignatureValidationError("Computed signature does not match"); } - var ttlValid = (int)(GetUnixEpochTimeInSeconds() - ttl) < Int32.Parse(data["timestamp"]); + var ttlValid = (int)(GetUnixEpochTimeInSeconds() - ttl) < Int32.Parse(parameters["timestamp"]); if (!ttlValid) { throw new SignatureValidationError("TTL Expired"); } - return data; + return parameters; } catch (Exception e) { throw new SignatureValidationError("Exception while validating toopher signature: " + e); diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 6d2a6f6..cb6790d 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -282,6 +282,27 @@ public void ValidatePostbackMissingSessionTokenFailsTest() var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); Assert.That(ex.Message, Is.StringContaining("Missing required keys: session_token")); } + + [Test] + public void ValidatePostbackWithoutStringArrayIsSuccessfulTest() + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + Dictionary data = new Dictionary() + { + {"foo", "bar"}, + {"timestamp", ((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}, + {"session_token", REQUEST_TOKEN}, + {"toopher_sig", "6d2c7GlQssGmeYYGpcf+V/kirOI="} + }; + try + { + Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, 5)); + } catch (Exception) + { + Assert.Fail("ValidatePostback overload for Dictionary was not successful"); + } + } } [TestFixture()] From 2ea651793fb4d8e05e840cb40bc1c1063926a85c Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Mar 2015 17:02:46 -0500 Subject: [PATCH 084/103] Set default resetEmail, requestToken and requesterMetadata to empty strings --- ToopherDotNet/ToopherDotNet.cs | 4 +-- ToopherDotNetTests/ToopherDotNetTests.cs | 43 +++++++++++++++++++----- 2 files changed, 37 insertions(+), 10 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index d61cf63..f9783be 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -68,7 +68,7 @@ public ToopherIframe(string consumerKey, string consumerSecret, string baseUrl = /// Optional, can be empty. Toopher will include this value in the signed data returned with the iFrame response. /// An optional Dictionary of extra parameters to provide to the API. /// A string URL that can be used to retrieve the Authentication iFrame by the user's browser. - public string GetAuthenticationUrl(string userName, string resetEmail, string requestToken, string actionName = "Log In", string requesterMetadata = "None", Dictionary extras = null) + public string GetAuthenticationUrl(string userName, string resetEmail="", string requestToken="", string actionName = "Log In", string requesterMetadata = "", Dictionary extras = null) { NameValueCollection parameters = new NameValueCollection(); long ttl; @@ -106,7 +106,7 @@ public string GetAuthenticationUrl(string userName, string resetEmail, string re /// Email address that the user has access to. In case the user has lost or cannot access their mobile device, Toopher will send a reset email to this address. /// An optional Dictionary of extra parameters to provide to the API. /// A string URL that can be used to retrieve the Pairing iFrame by the user's browser. - public string GetUserManagementUrl(string userName, string resetEmail, Dictionary extras = null) + public string GetUserManagementUrl(string userName, string resetEmail="", Dictionary extras = null) { NameValueCollection parameters = new NameValueCollection(); long ttl; diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index cb6790d..3d84ed4 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -132,33 +132,60 @@ public void ToopherBaseUrlTest() } [Test] - public void GetAuthenticationUrlTest() + public void GetAuthenticationUrlOnlyUsernameTest() { var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); OAuthTools.SetNonceOverride(OAUTH_NONCE); OAuthTools.SetDateOverride(TEST_DATE); - string expected = "https://api.toopher.test/v1/web/authenticate?action_name=Log+In&expires=1300&requester_metadata=None&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=YN%2BkKNTaoypsB37fsjvMS8vsG5A%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; - var authUrl = api.GetAuthenticationUrl("jdoe", "jdoe@example.com", REQUEST_TOKEN); + string expected = "https://api.toopher.test/v1/web/authenticate?action_name=Log+In&expires=1300&requester_metadata=&reset_email=&session_token=&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=NkaWUjEPRLwgsQMEJGsIQEpyRT4%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var authUrl = api.GetAuthenticationUrl("jdoe"); Assert.AreEqual(expected, authUrl); } [Test] - public void GetAuthenticationUrlWithExtrasTest() + public void GetAuthenticationUrlWithOptionalArgsTest() + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + OAuthTools.SetNonceOverride(OAUTH_NONCE); + OAuthTools.SetDateOverride(TEST_DATE); + string expected = "https://api.toopher.test/v1/web/authenticate?action_name=it+is+a+test&expires=1300&requester_metadata=metadata&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=2TydgMnUwWoiwfpljKpSaFg0Luo%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var authUrl = api.GetAuthenticationUrl("jdoe", "jdoe@example.com", REQUEST_TOKEN, "it is a test", "metadata"); + Assert.AreEqual(expected, authUrl); + } + + [Test] + public void GetAuthenticationUrlWithOptionalArgsAndExtrasTest() { Dictionary extras = new Dictionary(); extras.Add("allow_inline_pairing", "false"); + extras.Add("automation_allowed", "false"); + extras.Add("challenge_required", "true"); + extras.Add("ttl", "100"); var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); OAuthTools.SetNonceOverride(OAUTH_NONCE); OAuthTools.SetDateOverride(TEST_DATE); - string expected = "https://api.toopher.test/v1/web/authenticate?action_name=it+is+a+test&allow_inline_pairing=false&expires=1300&requester_metadata=None&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=W%2F2dcdsVc7YgdSCZuEo8ViHLlOo%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; - var authUrl = api.GetAuthenticationUrl("jdoe", "jdoe@example.com", REQUEST_TOKEN, "it is a test", "None", extras); + string expected = "https://api.toopher.test/v1/web/authenticate?action_name=it+is+a+test&allow_inline_pairing=false&automation_allowed=false&challenge_required=true&expires=1100&requester_metadata=metadata&reset_email=jdoe%40example.com&session_token=s9s7vsb&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=61dqeQNPFxNy8PyEFB9e5UfgN8s%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var authUrl = api.GetAuthenticationUrl("jdoe", "jdoe@example.com", REQUEST_TOKEN, "it is a test", "metadata", extras); Assert.AreEqual(expected, authUrl); } [Test] - public void GetUserManagementUrlTest() + public void GetUserManagementUrlOnlyUsernameTest() + { + var api = getToopherIframeApi(); + ToopherIframe.SetDateOverride(TEST_DATE); + OAuthTools.SetNonceOverride(OAUTH_NONCE); + OAuthTools.SetDateOverride(TEST_DATE); + string expected = "https://api.toopher.test/v1/web/manage_user?expires=1300&reset_email=&username=jdoe&v=2&oauth_consumer_key=abcdefg&oauth_nonce=12345678&oauth_signature=SA7CAUj%2B5QcGO%2BMmdPv9ubbaozk%3D&oauth_signature_method=HMAC-SHA1&oauth_timestamp=1000&oauth_version=1.0&"; + var userManagementUrl = api.GetUserManagementUrl("jdoe"); + Assert.AreEqual(expected, userManagementUrl); + } + + [Test] + public void GetUserManagementUrlWithEmailTest() { var api = getToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); @@ -170,7 +197,7 @@ public void GetUserManagementUrlTest() } [Test] - public void GetUserManagementUrlWithExtrasTest() + public void GetUserManagementUrlWithEmailAndExtrasTest() { Dictionary extras = new Dictionary(); extras.Add("ttl", "100"); From 612d67cdabfd4ca1ffa0593ae985f8b55371b1b5 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Mar 2015 17:08:24 -0500 Subject: [PATCH 085/103] Add Iframe version and expires to params in GetOauthUrl --- ToopherDotNet/ToopherDotNet.cs | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index f9783be..322d76f 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -81,13 +81,11 @@ public string GetAuthenticationUrl(string userName, string resetEmail="", string ttl = DEFAULT_TTL; } - parameters.Add("v", IFRAME_VERSION); parameters.Add("username", userName); parameters.Add("reset_email", resetEmail); parameters.Add("session_token", requestToken); parameters.Add("action_name", actionName); parameters.Add("requester_metadata", requesterMetadata); - parameters.Add("expires", (GetUnixEpochTimeInSeconds() + ttl).ToString()); if (extras != null) { @@ -96,7 +94,7 @@ public string GetAuthenticationUrl(string userName, string resetEmail="", string parameters.Add(kvp.Key, kvp.Value); } } - return GetOauthUrl(baseUrl + "web/authenticate", parameters); + return GetOauthUrl(baseUrl + "web/authenticate", parameters, ttl); } /// @@ -119,10 +117,8 @@ public string GetUserManagementUrl(string userName, string resetEmail="", Dictio ttl = DEFAULT_TTL; } - parameters.Add("v", IFRAME_VERSION); parameters.Add("username", userName); parameters.Add("reset_email", resetEmail); - parameters.Add("expires", (GetUnixEpochTimeInSeconds() + ttl).ToString()); if (extras != null) { @@ -131,7 +127,7 @@ public string GetUserManagementUrl(string userName, string resetEmail="", Dictio parameters.Add(kvp.Key, kvp.Value); } } - return GetOauthUrl(baseUrl + "web/manage_user", parameters); + return GetOauthUrl(baseUrl + "web/manage_user", parameters, ttl); } public Dictionary ValidatePostback(Dictionary parameters, string requestToken, long ttl = DEFAULT_TTL) @@ -213,8 +209,11 @@ public Dictionary ValidatePostback(Dictionary pa } } - private string GetOauthUrl(string url, NameValueCollection parameters) + private string GetOauthUrl(string url, NameValueCollection parameters, long ttl) { + parameters.Add("v", IFRAME_VERSION); + parameters.Add("expires", (GetUnixEpochTimeInSeconds() + ttl).ToString()); + OAuthRequest client = OAuthRequest.ForRequestToken(consumerKey, consumerSecret); client.RequestUrl = url; From b14895a655a29f03d31bc2e23410eecb55774248 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 11 Mar 2015 17:41:23 -0500 Subject: [PATCH 086/103] Use Int32.Parse for ttl --- ToopherDotNet/ToopherDotNet.cs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 322d76f..daefc0a 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -74,7 +74,7 @@ public string GetAuthenticationUrl(string userName, string resetEmail="", string long ttl; if (extras != null && extras.ContainsKey("ttl")) { - ttl = Convert.ToInt64(extras["ttl"]); + ttl = Int32.Parse(extras["ttl"]); extras.Remove("ttl"); } else { @@ -110,7 +110,7 @@ public string GetUserManagementUrl(string userName, string resetEmail="", Dictio long ttl; if (extras != null && extras.ContainsKey("ttl")) { - ttl = Convert.ToInt64(extras["ttl"]); + ttl = Int32.Parse(extras["ttl"]); extras.Remove("ttl"); } else { From b0d249380404412f52b7cc018ecaa8d892deba6a Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Mar 2015 09:25:24 -0500 Subject: [PATCH 087/103] Refactor ValidatePostback into smaller methods --- ToopherDotNet/ToopherDotNet.cs | 105 +++++++++++++---------- ToopherDotNetTests/ToopherDotNetTests.cs | 33 +++++-- 2 files changed, 84 insertions(+), 54 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index daefc0a..9a23d94 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -130,7 +130,7 @@ public string GetUserManagementUrl(string userName, string resetEmail="", Dictio return GetOauthUrl(baseUrl + "web/manage_user", parameters, ttl); } - public Dictionary ValidatePostback(Dictionary parameters, string requestToken, long ttl = DEFAULT_TTL) + public Dictionary ValidatePostback(Dictionary parameters, string requestToken, Dictionary extras = null) { Dictionary data = new Dictionary(); foreach (var entry in parameters) @@ -140,7 +140,7 @@ public Dictionary ValidatePostback(Dictionary data.Add(entry.Key, entry.Value[0]); } } - return ValidatePostback(data, requestToken, ttl); + return ValidatePostback(data, requestToken, extras); } /// @@ -150,62 +150,75 @@ public Dictionary ValidatePostback(Dictionary /// The request token /// Time-To-Live (seconds) to enforce on the Toopher API signature. This value sets the maximum duration between the Toopher API creating the signature and the signature being validated on your server. /// A Dictionary of the validated data if the signature is valid, or null if the signature is invalid. - public Dictionary ValidatePostback(Dictionary parameters, string requestToken, long ttl = DEFAULT_TTL) + public Dictionary ValidatePostback(Dictionary parameters, string requestToken, Dictionary extras = null) { try { - List missingKeys = new List(); - - if (!parameters.ContainsKey("toopher_sig")) - { - missingKeys.Add("toopher_sig"); - } - if (!parameters.ContainsKey("timestamp")) - { - missingKeys.Add("timestamp"); - } - if (!parameters.ContainsKey("session_token")) - { - missingKeys.Add("session_token"); - } - if (missingKeys.Count() > 0) - { - var keys = string.Join(", ", missingKeys.ToArray()); - throw new SignatureValidationError("Missing required keys: " + keys); - } + CheckForMissingKeys(parameters); + VerifySessionToken(parameters["session_token"], requestToken); + CheckIfSignatureIsExpired(parameters["timestamp"], extras); + ValidateSignature(parameters); + return parameters; + } catch (Exception e) + { + throw new SignatureValidationError("Exception while validating toopher signature: " + e); + } + } - if (parameters["session_token"] != requestToken) - { - throw new SignatureValidationError("Session token does not match expected value"); - } + private static void CheckForMissingKeys(Dictionary parameters) + { + List missingKeys = new List(); - string maybeSignature = parameters["toopher_sig"]; - parameters.Remove("toopher_sig"); - var signatureValid = false; - try - { - var computedSig = Signature(consumerSecret, parameters); - signatureValid = computedSig == maybeSignature; - } catch (Exception e) + string[] requiredKeys = {"toopher_sig", "timestamp", "session_token"}; + foreach (string key in requiredKeys) + { + if (!parameters.ContainsKey(key)) { - throw new SignatureValidationError("Error while calculating signature: " + e); + missingKeys.Add(key); } + } + if (missingKeys.Count() > 0) + { + var keys = string.Join(", ", missingKeys.ToArray()); + throw new SignatureValidationError("Missing required keys: " + keys); + } + } - if (!signatureValid) - { - throw new SignatureValidationError("Computed signature does not match"); - } + private static void VerifySessionToken(string sessionToken, string requestToken) + { + if (sessionToken != requestToken) + { + throw new SignatureValidationError("Session token does not match expected value"); + } + } - var ttlValid = (int)(GetUnixEpochTimeInSeconds() - ttl) < Int32.Parse(parameters["timestamp"]); - if (!ttlValid) - { - throw new SignatureValidationError("TTL Expired"); - } + private static void CheckIfSignatureIsExpired(string timestamp, Dictionary extras) + { + var ttl = (extras.ContainsKey("ttl")) ? Int32.Parse(extras["ttl"]) : DEFAULT_TTL; + var ttlValid = (int)(GetUnixEpochTimeInSeconds() - ttl) < Int32.Parse(timestamp); + if (!ttlValid) + { + throw new SignatureValidationError("TTL Expired"); + } + } - return parameters; + private void ValidateSignature(Dictionary parameters) + { + string maybeSignature = parameters["toopher_sig"]; + parameters.Remove("toopher_sig"); + var signatureValid = false; + try + { + var computedSig = Signature(consumerSecret, parameters); + signatureValid = computedSig == maybeSignature; } catch (Exception e) { - throw new SignatureValidationError("Exception while validating toopher signature: " + e); + throw new SignatureValidationError("Error while calculating signature: " + e); + } + + if (!signatureValid) + { + throw new SignatureValidationError("Computed signature does not match"); } } diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 3d84ed4..ac7c5f4 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -220,9 +220,11 @@ public void ValidatePostbackWithGoodSignatureIsSuccessfulTest() data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("session_token", new string[]{REQUEST_TOKEN}); data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); + Dictionary extras = new Dictionary(); + extras.Add("ttl", "5"); try { - Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, extras)); } catch (Exception) { Assert.Fail("Valid signature, timestamp, and session token did not return validated data"); @@ -239,7 +241,9 @@ public void ValidatePostbackWithBadSignatureFailsTest() data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("session_token", new string[]{REQUEST_TOKEN}); data.Add("toopher_sig", new string[]{"invalid"}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Dictionary extras = new Dictionary(); + extras.Add("ttl", "5"); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); Assert.That(ex.Message, Is.StringContaining("Computed signature does not match")); } @@ -253,7 +257,9 @@ public void ValidatePostbackWithExpiredSignatureFailsTest() data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("session_token", new string[]{REQUEST_TOKEN}); data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Dictionary extras = new Dictionary(); + extras.Add("ttl", "5"); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); Assert.That(ex.Message, Is.StringContaining("TTL Expired")); } @@ -267,7 +273,10 @@ public void ValidatePostbackWithInvalidSessionTokenFailsTest() data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("session_token", new string[]{"invalid token"}); data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Dictionary extras = new Dictionary(); + extras.Add("ttl", "5"); + + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); Assert.That(ex.Message, Is.StringContaining("Session token does not match expected value")); } @@ -280,7 +289,9 @@ public void ValidatePostbackMissingTimestampFailsTest() data.Add("foo", new string[]{"bar"}); data.Add("session_token", new string[]{REQUEST_TOKEN}); data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Dictionary extras = new Dictionary(); + extras.Add("ttl", "5"); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); Assert.That(ex.Message, Is.StringContaining("Missing required keys: timestamp")); } @@ -293,7 +304,9 @@ public void ValidatePostbackMissingSignatureFailsTest() data.Add("foo", new string[]{"bar"}); data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("session_token", new string[]{REQUEST_TOKEN}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Dictionary extras = new Dictionary(); + extras.Add("ttl", "5"); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); Assert.That(ex.Message, Is.StringContaining("Missing required keys: toopher_sig")); } @@ -306,7 +319,9 @@ public void ValidatePostbackMissingSessionTokenFailsTest() data.Add("foo", new string[]{"bar"}); data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Dictionary extras = new Dictionary(); + extras.Add("ttl", "5"); + var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); Assert.That(ex.Message, Is.StringContaining("Missing required keys: session_token")); } @@ -322,9 +337,11 @@ public void ValidatePostbackWithoutStringArrayIsSuccessfulTest() {"session_token", REQUEST_TOKEN}, {"toopher_sig", "6d2c7GlQssGmeYYGpcf+V/kirOI="} }; + Dictionary extras = new Dictionary(); + extras.Add("ttl", "5"); try { - Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, 5)); + Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, extras)); } catch (Exception) { Assert.Fail("ValidatePostback overload for Dictionary was not successful"); From 251c494394e20175bdb8bbbda2074c4d59be5f0c Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Mar 2015 09:30:43 -0500 Subject: [PATCH 088/103] Capitalize method names --- ToopherDotNetTests/ToopherDotNetTests.cs | 124 +++++++++++------------ 1 file changed, 62 insertions(+), 62 deletions(-) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index ac7c5f4..5252a43 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -100,7 +100,7 @@ public virtual void Init() WebClientMock.LastRequestData = null; } - public ToopherApi getToopherApi() + public ToopherApi GetToopherApi() { return new ToopherApi("key", "secret", null, typeof(WebClientMock)); } @@ -113,7 +113,7 @@ public class ToopherIframeTests : TestBase private const string OAUTH_NONCE = "12345678"; private DateTime TEST_DATE = new DateTime(1970, 1, 1, 0, 16, 40, 0); - private ToopherIframe getToopherIframeApi() + private ToopherIframe GetToopherIframeApi() { return new ToopherIframe(TOOPHER_CONSUMER_KEY, TOOPHER_CONSUMER_SECRET, DEFAULT_BASE_URL); } @@ -134,7 +134,7 @@ public void ToopherBaseUrlTest() [Test] public void GetAuthenticationUrlOnlyUsernameTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); OAuthTools.SetNonceOverride(OAUTH_NONCE); OAuthTools.SetDateOverride(TEST_DATE); @@ -146,7 +146,7 @@ public void GetAuthenticationUrlOnlyUsernameTest() [Test] public void GetAuthenticationUrlWithOptionalArgsTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); OAuthTools.SetNonceOverride(OAUTH_NONCE); OAuthTools.SetDateOverride(TEST_DATE); @@ -163,7 +163,7 @@ public void GetAuthenticationUrlWithOptionalArgsAndExtrasTest() extras.Add("automation_allowed", "false"); extras.Add("challenge_required", "true"); extras.Add("ttl", "100"); - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); OAuthTools.SetNonceOverride(OAUTH_NONCE); OAuthTools.SetDateOverride(TEST_DATE); @@ -175,7 +175,7 @@ public void GetAuthenticationUrlWithOptionalArgsAndExtrasTest() [Test] public void GetUserManagementUrlOnlyUsernameTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); OAuthTools.SetNonceOverride(OAUTH_NONCE); OAuthTools.SetDateOverride(TEST_DATE); @@ -187,7 +187,7 @@ public void GetUserManagementUrlOnlyUsernameTest() [Test] public void GetUserManagementUrlWithEmailTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); OAuthTools.SetNonceOverride(OAUTH_NONCE); OAuthTools.SetDateOverride(TEST_DATE); @@ -201,7 +201,7 @@ public void GetUserManagementUrlWithEmailAndExtrasTest() { Dictionary extras = new Dictionary(); extras.Add("ttl", "100"); - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); OAuthTools.SetNonceOverride(OAUTH_NONCE); OAuthTools.SetDateOverride(TEST_DATE); @@ -213,7 +213,7 @@ public void GetUserManagementUrlWithEmailAndExtrasTest() [Test] public void ValidatePostbackWithGoodSignatureIsSuccessfulTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); @@ -234,7 +234,7 @@ public void ValidatePostbackWithGoodSignatureIsSuccessfulTest() [Test] public void ValidatePostbackWithBadSignatureFailsTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); @@ -250,7 +250,7 @@ public void ValidatePostbackWithBadSignatureFailsTest() [Test] public void ValidatePostbackWithExpiredSignatureFailsTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(new DateTime(1970, 2, 1, 0, 16, 40, 0)); Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); @@ -266,7 +266,7 @@ public void ValidatePostbackWithExpiredSignatureFailsTest() [Test] public void ValidatePostbackWithInvalidSessionTokenFailsTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); @@ -283,7 +283,7 @@ public void ValidatePostbackWithInvalidSessionTokenFailsTest() [Test] public void ValidatePostbackMissingTimestampFailsTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); @@ -298,7 +298,7 @@ public void ValidatePostbackMissingTimestampFailsTest() [Test] public void ValidatePostbackMissingSignatureFailsTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); @@ -313,7 +313,7 @@ public void ValidatePostbackMissingSignatureFailsTest() [Test] public void ValidatePostbackMissingSessionTokenFailsTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); Dictionary data = new Dictionary(); data.Add("foo", new string[]{"bar"}); @@ -328,7 +328,7 @@ public void ValidatePostbackMissingSessionTokenFailsTest() [Test] public void ValidatePostbackWithoutStringArrayIsSuccessfulTest() { - var api = getToopherIframeApi(); + var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); Dictionary data = new Dictionary() { @@ -374,7 +374,7 @@ public void ToopherBaseUrlTest() [Test] public void CreatePairingWithPhraseTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair("some user", "awkward turtle"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); @@ -386,7 +386,7 @@ public void CreatePairingWithPhraseTest() [Test] public void CreateSmsPairingTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair("some user", "555-555-5555"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); @@ -398,7 +398,7 @@ public void CreateSmsPairingTest() [Test] public void CreateQrPairingTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair("some user"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); @@ -409,7 +409,7 @@ public void CreateQrPairingTest() [Test] public void ArbitraryParametersOnPairTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair("some user", "awkward turtle", extras: new Dictionary(){{"test_param", "42"}}); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); @@ -422,7 +422,7 @@ public void ArbitraryParametersOnPairTest() [Test] public void AuthenticateWithPairingIdTest() { - var api = getToopherApi(); + var api = GetToopherApi(); string pairingId = Guid.NewGuid().ToString(); WebClientMock.ReturnValue = @"{""id"":""" + pairingId + @""", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"",""requester_specified_id"":""requesterSpecifiedId"",""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; AuthenticationRequest auth = api.Authenticate(pairingId, "test terminal"); @@ -435,7 +435,7 @@ public void AuthenticateWithPairingIdTest() [Test] public void AuthenticateWithUsernameAndExtrasTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.Authenticate("some other user", requesterSpecifiedId: "requester specified id", extras: new Dictionary(){{ "random_key" , "42" }}); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); @@ -448,7 +448,7 @@ public void AuthenticateWithUsernameAndExtrasTest() [Test] public void ArbitraryParamtersOnAuthenticateTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.Authenticate("1", "test terminal", extras: new Dictionary(){{ "test_param", "42" }}); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); @@ -459,49 +459,49 @@ public void ArbitraryParamtersOnAuthenticateTest() [Test] public void GenerateAdvancedApiUsageFactory() { - var api = getToopherApi(); + var api = GetToopherApi(); Assert.IsInstanceOf(api.advanced); } [Test] public void GenerateAdvancedPairings() { - var api = getToopherApi(); + var api = GetToopherApi(); Assert.IsInstanceOf(api.advanced.pairings); } [Test] public void GenerateAdvancedRaw() { - var api = getToopherApi(); + var api = GetToopherApi(); Assert.IsInstanceOf(api.advanced.raw); } [Test] public void GenerateAdvancedAuthenticationRequests() { - var api = getToopherApi(); + var api = GetToopherApi(); Assert.IsInstanceOf(api.advanced.authenticationRequests); } [Test] public void GenerateAdvancedUsers() { - var api = getToopherApi(); + var api = GetToopherApi(); Assert.IsInstanceOf(api.advanced.users); } [Test] public void GenerateAdvancedUserTerminals() { - var api = getToopherApi(); + var api = GetToopherApi(); Assert.IsInstanceOf(api.advanced.userTerminals); } [Test] public void AdvancedPairingsGetByIdTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.advanced.pairings.GetById("1"); Assert.IsInstanceOf(pairing); @@ -515,7 +515,7 @@ public void AdvancedPairingsGetByIdTest() [Test] public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""random_key"":""84""}"; Pairing pairing = api.advanced.pairings.GetById("1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); @@ -529,7 +529,7 @@ public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest() [Test] public void AdvancedAuthenticationRequestsGetByIdTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.advanced.authenticationRequests.GetById("1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); @@ -544,7 +544,7 @@ public void AdvancedAuthenticationRequestsGetByIdTest() [Test] public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}, ""random_key"":""84""}"; AuthenticationRequest auth = api.advanced.authenticationRequests.GetById("1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); @@ -560,7 +560,7 @@ public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest() [Test] public void AdvancedUsersGetByIdTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = USER_RESPONSE; User user = api.advanced.users.GetById("1"); Assert.IsInstanceOf(user); @@ -573,7 +573,7 @@ public void AdvancedUsersGetByIdTest() [Test] public void AccessArbitraryKeysInAdvancedUsersGetByIdTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true, ""random_key"":""84""}"; User user = api.advanced.users.GetById("1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); @@ -586,7 +586,7 @@ public void AccessArbitraryKeysInAdvancedUsersGetByIdTest() [Test] public void AdvancedUsersGetByNameTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValueArray = @"[{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}]"; WebClientMock.ReturnValue = USER_RESPONSE; User user = api.advanced.users.GetByName("userName"); @@ -599,7 +599,7 @@ public void AdvancedUsersGetByNameTest() [Test] public void AdvancedUsersCreateTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = USER_RESPONSE; User user = api.advanced.users.Create("userName"); Assert.IsInstanceOf(user); @@ -612,7 +612,7 @@ public void AdvancedUsersCreateTest() [Test] public void AdvancedUsersCreateWithParamsTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = USER_RESPONSE; NameValueCollection parameters = new NameValueCollection(); parameters.Add("foo", "bar"); @@ -628,7 +628,7 @@ public void AdvancedUsersCreateWithParamsTest() [Test] public void AdvancedUserTerminalsGetByIdTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; UserTerminal userTerminal = api.advanced.userTerminals.GetById("1"); Assert.IsInstanceOf(userTerminal); @@ -642,7 +642,7 @@ public void AdvancedUserTerminalsGetByIdTest() [Test] public void AccessArbitraryKeysInAdvancedUserTerminalsGetByIdTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"",""random_key"":""84"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; UserTerminal userTerminal = api.advanced.userTerminals.GetById("1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); @@ -655,7 +655,7 @@ public void AccessArbitraryKeysInAdvancedUserTerminalsGetByIdTest() [Test] public void AdvancedUserTerminalsCreateTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; UserTerminal userTerminal = api.advanced.userTerminals.Create("userName", "userTerminalName", "requesterSpecifiedId"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); @@ -668,7 +668,7 @@ public void AdvancedUserTerminalsCreateTest() [Test] public void AdvancedUserTerminalsCreateWithParamsTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; NameValueCollection parameters = new NameValueCollection(); parameters.Add("foo", "bar"); @@ -685,7 +685,7 @@ public void AdvancedUserTerminalsCreateWithParamsTest() [ExpectedException(typeof(UserDisabledError))] public void DisabledUserRaisesCorrectErrorTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":704, ""error_message"":""The specified user has disabled Toopher authentication.""}"); api.Authenticate("some disabled user", requesterSpecifiedId: "some random string"); @@ -695,7 +695,7 @@ public void DisabledUserRaisesCorrectErrorTest() [ExpectedException(typeof(UserUnknownError))] public void UnknownUserRaisesCorrectErrorTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":705, ""error_message"":""No matching user exists.""}"); api.Authenticate("some unknown user", requesterSpecifiedId: "some random string"); @@ -705,7 +705,7 @@ public void UnknownUserRaisesCorrectErrorTest() [ExpectedException(typeof(TerminalUnknownError))] public void UnknownTerminalRaisesCorrectErrorTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":706, ""error_message"":""No matching terminal exists.""}"); api.Authenticate("some unknown user", requesterSpecifiedId: "some random string"); @@ -715,7 +715,7 @@ public void UnknownTerminalRaisesCorrectErrorTest() [ExpectedException(typeof(PairingDeactivatedError))] public void DeactivatedPairingRaisesCorrectErrorTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":601, ""error_message"":""This pairing has been deactivated.""}"); api.Authenticate("some disabled user", requesterSpecifiedId: "some random string"); @@ -725,7 +725,7 @@ public void DeactivatedPairingRaisesCorrectErrorTest() [ExpectedException(typeof(PairingDeactivatedError))] public void UnauthorizedPairingRaisesCorrectErrorTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnException = makeError((HttpStatusCode)409, @"{""error_code"":601, ""error_message"":""This pairing has not been authorized to authenticate.""}"); api.Authenticate("some unauthorized user", requesterSpecifiedId: "some random string"); @@ -740,7 +740,7 @@ public class PairingTests : TestBase [Test] public void CreatePairingTest() { - var api = getToopherApi(); + var api = GetToopherApi(); Pairing pairing = new Pairing(PAIRING_DICT, api); Assert.AreEqual(pairing.id, "1"); Assert.IsFalse(pairing.pending); @@ -753,7 +753,7 @@ public void CreatePairingTest() [Test] public void PairingRefreshFromServerTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""enabled"":false, ""user"":{""id"":""1"",""name"":""userNameChanged"", ""toopher_authentication_enabled"":true}}"; Pairing pairing = new Pairing(PAIRING_DICT, api); pairing.RefreshFromServer(); @@ -768,7 +768,7 @@ public void PairingRefreshFromServerTest() [Test] public void PairingGetResetLinkTest() { - var api = getToopherApi(); + var api = GetToopherApi(); string link = "http://api.toopher.test/v1/pairings/1/reset?reset_authorization=abcde"; WebClientMock.ReturnValue = @"{""url"":""" + link + @"""}"; Pairing pairing = new Pairing(PAIRING_DICT, api); @@ -780,7 +780,7 @@ public void PairingGetResetLinkTest() [Test] public void PairingEmailResetLinkTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = @"{}"; Pairing pairing = new Pairing(PAIRING_DICT, api); pairing.EmailResetLink("test@test.com"); @@ -791,7 +791,7 @@ public void PairingEmailResetLinkTest() [Test] public void PairingGetQrCodeImage() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = @"{}"; Pairing pairing = new Pairing(PAIRING_DICT, api); pairing.GetQrCodeImage(); @@ -808,7 +808,7 @@ public class AuthenticationRequestTests : TestBase [Test] public void CreateAuthenticationRequestTest() { - var api = getToopherApi(); + var api = GetToopherApi(); AuthenticationRequest auth = new AuthenticationRequest(AUTH_REQUEST_DICT, api); Assert.AreEqual(auth.id, "1"); Assert.IsTrue(auth.pending); @@ -829,7 +829,7 @@ public void CreateAuthenticationRequestTest() [Test] public void AuthenticationRequestRefreshFromServerTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":true, ""granted"":true, ""automated"":false, ""reason_code"":2, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal CHANGED"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user CHANGED"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName CHANGED""}}"; AuthenticationRequest auth = new AuthenticationRequest(AUTH_REQUEST_DICT, api); auth.RefreshFromServer(); @@ -845,7 +845,7 @@ public void AuthenticationRequestRefreshFromServerTest() [Test] public void AuthenticationRequestGrantWithOtpTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = new AuthenticationRequest(AUTH_REQUEST_DICT, api); auth.GrantWithOtp("123456"); @@ -865,7 +865,7 @@ public class UserTests : TestBase [Test] public void CreateUserTest() { - var api = getToopherApi(); + var api = GetToopherApi(); Toopher.User user = new Toopher.User(USER_DICT, api); Assert.AreEqual(user.id, "1"); Assert.AreEqual(user.name, "userName"); @@ -875,7 +875,7 @@ public void CreateUserTest() [Test] public void UserRefreshFromServerTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userNameChanged"", ""toopher_authentication_enabled"":false}"; User user = new User(USER_DICT, api); user.RefreshFromServer(); @@ -889,7 +889,7 @@ public void UserRefreshFromServerTest() [Test] public void UserEnableToopherAuthentication() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = USER_RESPONSE; User user = new User(USER_DICT, api); user.EnableToopherAuthentication(); @@ -902,7 +902,7 @@ public void UserEnableToopherAuthentication() [Test] public void UserDisableToopherAuthentication() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":false}"; User user = new User(USER_DICT, api); user.DisableToopherAuthentication(); @@ -915,7 +915,7 @@ public void UserDisableToopherAuthentication() [Test] public void UserResetTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = USER_RESPONSE; User user = new User(USER_DICT, api); user.Reset(); @@ -933,7 +933,7 @@ public class UserTerminalTests : TestBase [Test] public void CreateUserTerminalTest() { - var api = getToopherApi(); + var api = GetToopherApi(); UserTerminal userTerminal = new UserTerminal(USER_TERMINAL_DICT, api); Assert.AreEqual(userTerminal.id, "1"); Assert.AreEqual(userTerminal.name, "userTerminalName"); @@ -946,7 +946,7 @@ public void CreateUserTerminalTest() [Test] public void UserTerminalRefreshFromServerTest() { - var api = getToopherApi(); + var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalNameChanged"", ""requester_specified_id"":""requesterSpecifiedId"", ""user"":{""id"":""1"", ""name"":""userNameChanged"", ""toopher_authentication_enabled"":false}}"; UserTerminal userTerminal = new UserTerminal(USER_TERMINAL_DICT, api); userTerminal.RefreshFromServer(); From 97f829b1045e9119eccac1f987ca60c78f227340 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Mar 2015 13:06:20 -0500 Subject: [PATCH 089/103] Cast to IDictionary instead of JsonObject in AuthenticationRequest, Pairing and UserTerminal constructors --- ToopherDotNet/ToopherDotNet.cs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 9a23d94..5eb1a24 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -769,7 +769,7 @@ public Pairing(IDictionary response, ToopherApi toopherApi) this.id = (string)response["id"]; this.pending = (bool)response["pending"]; this.enabled = (bool)response["enabled"]; - this.user = new User((JsonObject)response["user"], toopherApi); + this.user = new User((IDictionary)response["user"], toopherApi); } catch (Exception ex) { throw new RequestError("Could not parse pairing from response", ex); @@ -918,9 +918,9 @@ public AuthenticationRequest(IDictionary response, ToopherApi to this.automated = (bool)response["automated"]; this.reasonCode = Convert.ToInt32(response["reason_code"]); this.reason = (string)response["reason"]; - this.action = new Action((JsonObject)response["action"]); - this.terminal = new UserTerminal((JsonObject)response["terminal"], toopherApi); - this.user = new User((JsonObject)response["user"], toopherApi); + this.action = new Action((IDictionary)response["action"]); + this.terminal = new UserTerminal((IDictionary)response["terminal"], toopherApi); + this.user = new User((IDictionary)response["user"], toopherApi); } catch (Exception ex) { throw new RequestError("Could not parse authentication request from response", ex); @@ -1132,7 +1132,7 @@ public UserTerminal(IDictionary response, ToopherApi toopherApi) this.id = (string)response["id"]; this.name = (string)response["name"]; this.requesterSpecifiedId = (string)response["requester_specified_id"]; - this.user = new User((JsonObject)response["user"], toopherApi); + this.user = new User((IDictionary)response["user"], toopherApi); } catch (Exception ex) { throw new RequestError("Could not parse user terminal from response", ex); From 7578519c1d60b5cd7491113cbbddd3bb7d6e4f56 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 12 Mar 2015 17:09:01 -0500 Subject: [PATCH 090/103] Replace ValidatePostback with ProcessPostback --- ToopherDotNet/ToopherDotNet.cs | 134 +++++++++-- ToopherDotNetTests/ToopherDotNetTests.cs | 276 +++++++++++++++-------- 2 files changed, 300 insertions(+), 110 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 5eb1a24..5b460ec 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -130,27 +130,49 @@ public string GetUserManagementUrl(string userName, string resetEmail="", Dictio return GetOauthUrl(baseUrl + "web/manage_user", parameters, ttl); } - public Dictionary ValidatePostback(Dictionary parameters, string requestToken, Dictionary extras = null) + /// + /// Verify the authenticity of data returned from the Toopher iFrame + /// + /// The data returned from the iFrame. + /// The request token + /// An optional Dictionary of extra parameters. + /// An , or object. + public Object ProcessPostback(Dictionary parameters, string requestToken = "", Dictionary extras = null) { - Dictionary data = new Dictionary(); - foreach (var entry in parameters) + Dictionary toopherData = UrlDecodeParameters(parameters); + if (toopherData.ContainsKey("error_code")) { - if (entry.Value.Length > 0) + var errorCode = Int32.Parse(toopherData["error_code"]); + var errorMessage = toopherData["error_message"]; + if (errorCode == UserDisabledError.ERROR_CODE) + { + throw new UserDisabledError(); + } else + { + throw new RequestError(errorMessage); + } + } else + { + ValidatePostback(toopherData, requestToken, extras); + ToopherApi api = new ToopherApi(consumerKey, consumerSecret); + var resourceType = toopherData["resource_type"]; + if (resourceType == "authentication_request") + { + return new AuthenticationRequest(CreateAuthenticationRequestDict(toopherData), api); + } else if (resourceType == "pairing") + { + return new Pairing(CreatePairingDict(toopherData), api); + } else if (resourceType == "requester_user") + { + return new User(CreateUserDict(toopherData), api); + } else { - data.Add(entry.Key, entry.Value[0]); + throw new RequestError ("The postback resource type is not valid: " + resourceType); } } - return ValidatePostback(data, requestToken, extras); } - /// - /// Verify the authenticity of data returned from the Toopher iFrame by validating the crytographic signature. - /// - /// The data returned from the iFrame. - /// The request token - /// Time-To-Live (seconds) to enforce on the Toopher API signature. This value sets the maximum duration between the Toopher API creating the signature and the signature being validated on your server. - /// A Dictionary of the validated data if the signature is valid, or null if the signature is invalid. - public Dictionary ValidatePostback(Dictionary parameters, string requestToken, Dictionary extras = null) + private void ValidatePostback(Dictionary parameters, string requestToken, Dictionary extras) { try { @@ -158,14 +180,13 @@ public Dictionary ValidatePostback(Dictionary pa VerifySessionToken(parameters["session_token"], requestToken); CheckIfSignatureIsExpired(parameters["timestamp"], extras); ValidateSignature(parameters); - return parameters; } catch (Exception e) { throw new SignatureValidationError("Exception while validating toopher signature: " + e); } } - private static void CheckForMissingKeys(Dictionary parameters) + private void CheckForMissingKeys(Dictionary parameters) { List missingKeys = new List(); @@ -184,17 +205,17 @@ private static void CheckForMissingKeys(Dictionary parameters) } } - private static void VerifySessionToken(string sessionToken, string requestToken) + private void VerifySessionToken(string sessionToken, string requestToken) { - if (sessionToken != requestToken) + if (!String.IsNullOrEmpty(requestToken) && sessionToken != requestToken) { throw new SignatureValidationError("Session token does not match expected value"); } } - private static void CheckIfSignatureIsExpired(string timestamp, Dictionary extras) + private void CheckIfSignatureIsExpired(string timestamp, Dictionary extras) { - var ttl = (extras.ContainsKey("ttl")) ? Int32.Parse(extras["ttl"]) : DEFAULT_TTL; + var ttl = (extras != null && extras.ContainsKey("ttl")) ? Int32.Parse(extras["ttl"]) : DEFAULT_TTL; var ttlValid = (int)(GetUnixEpochTimeInSeconds() - ttl) < Int32.Parse(timestamp); if (!ttlValid) { @@ -222,6 +243,60 @@ private void ValidateSignature(Dictionary parameters) } } + private IDictionary CreateAuthenticationRequestDict(Dictionary parameters) + { + return new Dictionary{ + {"id", parameters["id"]}, + {"pending", parameters["pending"] == "true"}, + {"granted", parameters["granted"] == "true"}, + {"automated", parameters["automated"] == "true"}, + {"reason_code", parameters["reason_code"]}, + {"reason", parameters["reason"]}, + {"terminal", new Dictionary{ + {"id", parameters["terminal_id"]}, + {"name", parameters["terminal_name"]}, + {"requester_specified_id", parameters["terminal_requester_specified_id"]}, + {"user", new Dictionary{ + {"id", parameters["pairing_user_id"]}, + {"name", parameters["user_name"]}, + {"toopher_authentication_enabled", parameters["user_toopher_authentication_enabled"] == "true"} + }} + }}, + {"user", new Dictionary{ + {"id", parameters["pairing_user_id"]}, + {"name", parameters["user_name"]}, + {"toopher_authentication_enabled", parameters["user_toopher_authentication_enabled"] == "true"} + }}, + {"action", new Dictionary{ + {"id", parameters["action_id"]}, + {"name", parameters["action_name"]} + }} + }; + } + + private IDictionary CreatePairingDict(Dictionary parameters) + { + return new Dictionary{ + {"id", parameters["id"]}, + {"enabled", parameters["enabled"] == "true"}, + {"pending", parameters["pending"] == "true"}, + {"user", new Dictionary{ + {"id", parameters["pairing_user_id"]}, + {"name", parameters["user_name"]}, + {"toopher_authentication_enabled", parameters["user_toopher_authentication_enabled"] == "true"} + }} + }; + } + + private IDictionary CreateUserDict(Dictionary parameters) + { + return new Dictionary{ + {"id", parameters["id"]}, + {"name", parameters["name"]}, + {"toopher_authentication_enabled", parameters["toopher_authentication_enabled"] == "true"} + }; + } + private string GetOauthUrl(string url, NameValueCollection parameters, long ttl) { parameters.Add("v", IFRAME_VERSION); @@ -243,7 +318,7 @@ private static int GetUnixEpochTimeInSeconds() private static string Signature(string secret, Dictionary data) { - Dictionary sortedData = data.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => x.Value); + Dictionary sortedData = data.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => OAuthTools.UrlEncodeStrict(x.Value).Replace("%20", "+")); string joinedString = string.Join("&", (sortedData.Select(d => d.Key + "=" + d.Value).ToArray())); byte[] keyBytes = Encoding.UTF8.GetBytes(secret); @@ -266,6 +341,23 @@ private static string UrlEncodeParameters(NameValueCollection parameters) collection.Sort((x, y) => x.Name.Equals(y.Name) ? x.Value.CompareTo(y.Value) : x.Name.CompareTo(y.Name)); return OAuthTools.Concatenate(collection, "=", "&"); } + + private static Dictionary UrlDecodeParameters(Dictionary parameters) + { + Dictionary toopher_data = new Dictionary(); + string[] splitParameters = parameters["toopher_iframe_data"].Split('&'); + foreach (var parameter in splitParameters) + { + string[] data = parameter.Split('='); + string value = Uri.UnescapeDataString(data[1]); + if (data[0] != "toopher_sig") + { + value = value.Replace("+", " "); + } + toopher_data.Add(data[0], value); + } + return toopher_data; + } } public class SignatureValidationError: Exception diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 5252a43..7eefc7d 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -118,6 +118,75 @@ private ToopherIframe GetToopherIframeApi() return new ToopherIframe(TOOPHER_CONSUMER_KEY, TOOPHER_CONSUMER_SECRET, DEFAULT_BASE_URL); } + private NameValueCollection GetAuthenticationRequestData() + { + return new NameValueCollection(){ + {"id", "1"}, + {"pending", "false"}, + {"granted", "true"}, + {"automated", "false"}, + {"reason", "it is a test"}, + {"reason_code", "100"}, + {"terminal_id", "1"}, + {"terminal_name", "terminal name"}, + {"terminal_requester_specified_id", "requester specified id"}, + {"pairing_user_id", "1"}, + {"user_name", "user name"}, + {"user_toopher_authentication_enabled", "true"}, + {"action_id", "1"}, + {"action_name", "action name"}, + {"toopher_sig", "s+fYUtChrNMjES5Xa+755H7BQKE="}, + {"session_token", REQUEST_TOKEN}, + {"timestamp", "1000"}, + {"resource_type", "authentication_request"}, + }; + } + + private NameValueCollection GetPairingData() + { + return new NameValueCollection(){ + {"id", "1"}, + {"enabled", "true"}, + {"pending", "false"}, + {"pairing_user_id", "1"}, + {"user_name", "user name"}, + {"user_toopher_authentication_enabled", "true"}, + {"toopher_sig", "ucwKhkPpN4VxNbx3dMypWzi4tBg="}, + {"session_token", REQUEST_TOKEN}, + {"timestamp", "1000"}, + {"resource_type", "pairing"} + }; + } + + private NameValueCollection GetUserData() + { + return new NameValueCollection() + { + {"id", "1"}, + {"name", "user name"}, + {"toopher_authentication_enabled", "true"}, + {"toopher_sig", "RszgG9QE1rF9t7DVTGg+1I25yHM="}, + {"session_token", REQUEST_TOKEN}, + {"timestamp", "1000"}, + {"resource_type", "requester_user"} + }; + } + + private Dictionary GetUrlencodedData(NameValueCollection parameters) + { + WebParameterCollection collection = new WebParameterCollection(parameters); + foreach (var parameter in collection) + { + parameter.Value = OAuthTools.UrlEncodeStrict(parameter.Value).Replace("%20", "+"); + } + collection.Sort((x, y) => x.Name.Equals(y.Name) ? x.Value.CompareTo(y.Value) : x.Name.CompareTo(y.Name)); + var encodedParameters = OAuthTools.Concatenate(collection, "=", "&"); + + Dictionary data = new Dictionary(); + data.Add("toopher_iframe_data", encodedParameters); + return data; + } + [Test] public void ToopherIframeVersionTest() { @@ -211,141 +280,170 @@ public void GetUserManagementUrlWithEmailAndExtrasTest() } [Test] - public void ValidatePostbackWithGoodSignatureIsSuccessfulTest() + public void ProcessPostbackWithGoodSignatureReturnsAuthenticationRequest() { - var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary(); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("session_token", new string[]{REQUEST_TOKEN}); - data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - Dictionary extras = new Dictionary(); - extras.Add("ttl", "5"); - try - { - Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, extras)); - } catch (Exception) - { - Assert.Fail("Valid signature, timestamp, and session token did not return validated data"); - } + var authData = GetAuthenticationRequestData(); + var api = GetToopherIframeApi(); + AuthenticationRequest authenticationRequest = (AuthenticationRequest) api.ProcessPostback(GetUrlencodedData(authData), REQUEST_TOKEN); + Assert.AreEqual(authenticationRequest.id, authData["id"]); + Assert.IsFalse(authenticationRequest.pending); + Assert.IsTrue(authenticationRequest.granted); + Assert.IsFalse(authenticationRequest.automated); + Assert.AreEqual(authenticationRequest.reasonCode, Convert.ToInt32(authData["reason_code"])); + Assert.AreEqual(authenticationRequest.action.id, authData["action_id"]); + Assert.AreEqual(authenticationRequest.action.name,authData["action_name"]); + Assert.AreEqual(authenticationRequest.terminal.id,authData["terminal_id"]); + Assert.AreEqual(authenticationRequest.terminal.name,authData["terminal_name"]); + Assert.AreEqual(authenticationRequest.terminal.requesterSpecifiedId,authData["terminal_requester_specified_id"]); + Assert.AreEqual(authenticationRequest.user.id,authData["pairing_user_id"]); + Assert.AreEqual(authenticationRequest.user.name,authData["user_name"]); + Assert.IsTrue(authenticationRequest.user.toopherAuthenticationEnabled); } [Test] - public void ValidatePostbackWithBadSignatureFailsTest() + public void ProcessPostbackWithGoodSignatureReturnsPairing() { + ToopherIframe.SetDateOverride(TEST_DATE); + var pairingData = GetPairingData(); var api = GetToopherIframeApi(); + Pairing pairing = (Pairing) api.ProcessPostback(GetUrlencodedData(pairingData), REQUEST_TOKEN); + Assert.AreEqual(pairing.id, pairingData["id"]); + Assert.IsTrue(pairing.enabled); + Assert.IsFalse(pairing.pending); + Assert.AreEqual(pairing.user.id, pairingData["pairing_user_id"]); + Assert.AreEqual(pairing.user.name, pairingData["user_name"]); + Assert.IsTrue(pairing.user.toopherAuthenticationEnabled); + } + + [Test] + public void ProcessPostbackWithGoodSignatureReturnsUser() + { ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary(); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("session_token", new string[]{REQUEST_TOKEN}); - data.Add("toopher_sig", new string[]{"invalid"}); - Dictionary extras = new Dictionary(); - extras.Add("ttl", "5"); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); - Assert.That(ex.Message, Is.StringContaining("Computed signature does not match")); + var userData = GetUserData(); + var api = GetToopherIframeApi(); + User user = (User) api.ProcessPostback(GetUrlencodedData(userData), REQUEST_TOKEN); + Assert.AreEqual(user.id, userData["id"]); + Assert.AreEqual(user.name, userData["name"]); + Assert.IsTrue(user.toopherAuthenticationEnabled); } [Test] - public void ValidatePostbackWithExpiredSignatureFailsTest() + public void ProcessPostbackWithExtrasReturnsAuthenticationRequest() { + ToopherIframe.SetDateOverride(TEST_DATE); var api = GetToopherIframeApi(); - ToopherIframe.SetDateOverride(new DateTime(1970, 2, 1, 0, 16, 40, 0)); - Dictionary data = new Dictionary(); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("session_token", new string[]{REQUEST_TOKEN}); - data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); Dictionary extras = new Dictionary(); extras.Add("ttl", "5"); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); - Assert.That(ex.Message, Is.StringContaining("TTL Expired")); + var authRequest = api.ProcessPostback(GetUrlencodedData(GetAuthenticationRequestData()), REQUEST_TOKEN, extras); + Assert.IsInstanceOf(authRequest); } [Test] - public void ValidatePostbackWithInvalidSessionTokenFailsTest() + public void ProcessPostbackWithoutRequestTokenReturnsAuthenticationRequest() { + ToopherIframe.SetDateOverride(TEST_DATE); var api = GetToopherIframeApi(); + var authRequest = api.ProcessPostback(GetUrlencodedData(GetAuthenticationRequestData())); + Assert.IsInstanceOf(authRequest); + } + + [Test] + public void ProcessPostbackWithBadSignatureThrowsError() + { ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary(); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("session_token", new string[]{"invalid token"}); - data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - Dictionary extras = new Dictionary(); - extras.Add("ttl", "5"); + var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Set("toopher_sig", "invalid"); + var ex = Assert.Throws(() => api.ProcessPostback(GetUrlencodedData(authData))); + Assert.That(ex.Message, Is.StringContaining("Computed signature does not match")); + } - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); - Assert.That(ex.Message, Is.StringContaining("Session token does not match expected value")); + [Test] + public void ProcessPostbackWithExpiredSignatureThrowsError() + { + ToopherIframe.SetDateOverride(new DateTime(1970, 3, 1, 0, 16, 40, 0)); + var api = GetToopherIframeApi(); + var ex = Assert.Throws(() => api.ProcessPostback(GetUrlencodedData(GetAuthenticationRequestData()))); + Assert.That(ex.Message, Is.StringContaining("TTL Expired")); } [Test] - public void ValidatePostbackMissingTimestampFailsTest() + public void ProcessPostbackMissingSignatureThrowsError() { + ToopherIframe.SetDateOverride(TEST_DATE); var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Remove("toopher_sig"); + var ex = Assert.Throws(() => api.ProcessPostback(GetUrlencodedData(authData))); + Assert.That(ex.Message, Is.StringContaining("Missing required keys: toopher_sig")); + } + + [Test] + public void ProcessPostbackMissingTimestampThrowsError() + { ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary(); - data.Add("foo", new string[]{"bar"}); - data.Add("session_token", new string[]{REQUEST_TOKEN}); - data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - Dictionary extras = new Dictionary(); - extras.Add("ttl", "5"); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); + var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Remove("timestamp"); + var ex = Assert.Throws(() => api.ProcessPostback(GetUrlencodedData(authData))); Assert.That(ex.Message, Is.StringContaining("Missing required keys: timestamp")); } [Test] - public void ValidatePostbackMissingSignatureFailsTest() + public void ProcessPostbackMissingSessionTokenThrowsError() { - var api = GetToopherIframeApi(); ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary(); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("session_token", new string[]{REQUEST_TOKEN}); - Dictionary extras = new Dictionary(); - extras.Add("ttl", "5"); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); - Assert.That(ex.Message, Is.StringContaining("Missing required keys: toopher_sig")); + var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Remove("session_token"); + var ex = Assert.Throws(() => api.ProcessPostback(GetUrlencodedData(authData))); + Assert.That(ex.Message, Is.StringContaining("Missing required keys: session_token")); } [Test] - public void ValidatePostbackMissingSessionTokenFailsTest() + public void ProcessPostbackWithInvalidSessionTokenThrowsError() { + ToopherIframe.SetDateOverride(TEST_DATE); var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Set("session_token", "invalid"); + var ex = Assert.Throws(() => api.ProcessPostback(GetUrlencodedData(authData), REQUEST_TOKEN)); + Assert.That(ex.Message, Is.StringContaining("Session token does not match expected value")); + } + + [Test] + public void ProcessPostbackWithInvalidResourceTypeThrowsError() + { ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary(); - data.Add("foo", new string[]{"bar"}); - data.Add("timestamp", new string[]{((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}); - data.Add("toopher_sig", new string[]{"6d2c7GlQssGmeYYGpcf+V/kirOI="}); - Dictionary extras = new Dictionary(); - extras.Add("ttl", "5"); - var ex = Assert.Throws(() => api.ValidatePostback(data, REQUEST_TOKEN, extras)); - Assert.That(ex.Message, Is.StringContaining("Missing required keys: session_token")); + var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Set("resource_type", "invalid"); + authData.Set("toopher_sig", "xEY+oOtJcdMsmTLp6eOy9isO/xQ="); + var ex = Assert.Throws(() => api.ProcessPostback(GetUrlencodedData(authData), REQUEST_TOKEN)); + Assert.That(ex.Message, Is.StringContaining("The postback resource type is not valid: invalid")); } [Test] - public void ValidatePostbackWithoutStringArrayIsSuccessfulTest() + public void ProcessPostbackWith704ErrorCodeThrowsError() { + ToopherIframe.SetDateOverride(TEST_DATE); var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Set("error_code", "704"); + authData.Set("error_message", "The specified user has disabled Toopher authentication."); + Assert.Throws(() => api.ProcessPostback(GetUrlencodedData(authData), REQUEST_TOKEN)); + } + + [Test] + public void ProcessPostbackWithNon704ErrorCodeThrowsError() + { ToopherIframe.SetDateOverride(TEST_DATE); - Dictionary data = new Dictionary() - { - {"foo", "bar"}, - {"timestamp", ((int)(TEST_DATE - new DateTime(1970, 1, 1)).TotalSeconds).ToString()}, - {"session_token", REQUEST_TOKEN}, - {"toopher_sig", "6d2c7GlQssGmeYYGpcf+V/kirOI="} - }; - Dictionary extras = new Dictionary(); - extras.Add("ttl", "5"); - try - { - Assert.IsNotNull(api.ValidatePostback(data, REQUEST_TOKEN, extras)); - } catch (Exception) - { - Assert.Fail("ValidatePostback overload for Dictionary was not successful"); - } + var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Set("error_code", "999"); + authData.Set("error_message", "This is a test."); + Assert.Throws(() => api.ProcessPostback(GetUrlencodedData(authData), REQUEST_TOKEN)); } } From 1077dad313286d8c3878241a162b29e12024d5ec Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 18 Mar 2015 10:43:23 -0500 Subject: [PATCH 091/103] Add ToopherIframe.IsAuthenticationGranted --- ToopherDotNet/ToopherDotNet.cs | 28 +++++++++ ToopherDotNetTests/ToopherDotNetTests.cs | 76 ++++++++++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 5b460ec..95c1f29 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -130,6 +130,34 @@ public string GetUserManagementUrl(string userName, string resetEmail="", Dictio return GetOauthUrl(baseUrl + "web/manage_user", parameters, ttl); } + /// + /// Evaluate whether AuthenticationRequest has been granted + /// + /// The data returned from the iFrame. + /// The request token + /// An optional Dictionary of extra parameters. + /// true or false + public bool IsAuthenticationGranted(Dictionary parameters, string requestToken = "", Dictionary extras = null) + { + try + { + AuthenticationRequest authenticationRequest = (AuthenticationRequest) ProcessPostback(parameters, requestToken, extras); + if (!authenticationRequest.pending && authenticationRequest.granted) + { + return true; + } else + { + return false; + } + } catch (UserDisabledError) + { + return true; + } catch (Exception) + { + return false; + } + } + /// /// Verify the authenticity of data returned from the Toopher iFrame /// diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 7eefc7d..1295445 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -279,6 +279,82 @@ public void GetUserManagementUrlWithEmailAndExtrasTest() Assert.AreEqual(expected, userManagementUrl); } + [Test] + public void IsAuthenticationGrantedWithAuthenticationRequestGrantedReturnsTrue() + { + var api = GetToopherIframeApi(); + Assert.IsTrue(api.IsAuthenticationGranted(GetUrlencodedData(GetAuthenticationRequestData()), REQUEST_TOKEN)); + } + + [Test] + public void IsAuthenticationGrantedWithAuthenticationRequestGrantedAndExtrasReturnsTrue() + { + var api = GetToopherIframeApi(); + Dictionary extras = new Dictionary(); + extras.Add("ttl", "5"); + Assert.IsTrue(api.IsAuthenticationGranted(GetUrlencodedData(GetAuthenticationRequestData()), REQUEST_TOKEN, extras)); + } + + [Test] + public void IsAuthenticationGrantedWithNoTokenReturnsTrue() + { + var api = GetToopherIframeApi(); + Assert.IsTrue(api.IsAuthenticationGranted(GetUrlencodedData(GetAuthenticationRequestData()))); + } + + [Test] + public void IsAuthenticationGrantedWithAuthenticationRequestNotGrantedReturnsFalse() + { + var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Set("granted", "false"); + authData.Set("toopher_sig", "nADNKdly9zA2IpczD6gvDumM48I="); + Assert.IsFalse(api.IsAuthenticationGranted(GetUrlencodedData(authData))); + } + + [Test] + public void IsAuthenticationGrantedWithPairingReturnsFalse() + { + var api = GetToopherIframeApi(); + Assert.IsFalse(api.IsAuthenticationGranted(GetUrlencodedData(GetPairingData()))); + } + + [Test] + public void IsAuthenticationGrantedWithUserReturnsFalse() + { + var api = GetToopherIframeApi(); + Assert.IsFalse(api.IsAuthenticationGranted(GetUrlencodedData(GetUserData()))); + } + + [Test] + public void IsAuthenticationGrantedWithSignatureValidationErrorReturnsFalse() + { + var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Remove("id"); + Assert.IsFalse(api.IsAuthenticationGranted(GetUrlencodedData(authData))); + } + + [Test] + public void IsAuthenticationGrantedWithRequestErrorReturnsFalse() + { + var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Set("resource_type", "invalid"); + authData.Set("toopher_sig", "xEY+oOtJcdMsmTLp6eOy9isO/xQ="); + Assert.IsFalse(api.IsAuthenticationGranted(GetUrlencodedData(authData))); + } + + [Test] + public void IsAuthenticationGrantedWithUserDisabledErrorReturnsTrue() + { + var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Set("error_code", "704"); + authData.Set("error_message", "The specified user has disabled Toopher authentication."); + Assert.IsTrue(api.IsAuthenticationGranted(GetUrlencodedData(authData))); + } + [Test] public void ProcessPostbackWithGoodSignatureReturnsAuthenticationRequest() { From f959ea745f89debf35c280367b8e807d23cb1f6e Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Fri, 20 Mar 2015 16:21:16 -0500 Subject: [PATCH 092/103] Add test for IsAuthenticationGranted when auth request is granted and pending --- ToopherDotNetTests/ToopherDotNetTests.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 1295445..d302228 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -312,6 +312,18 @@ public void IsAuthenticationGrantedWithAuthenticationRequestNotGrantedReturnsFal Assert.IsFalse(api.IsAuthenticationGranted(GetUrlencodedData(authData))); } + [Test] + public void IsAuthenticationGrantedWithAuthenticationRequestGrantedAndPendingReturnsFalse() + { + var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Set("granted", "true"); + authData.Set("pending", "true"); + authData.Set("toopher_sig", "vmWBQCy8Py5PVkMZRppbCG7cm0w="); + Console.WriteLine("here's the test"); + Assert.IsFalse(api.IsAuthenticationGranted(GetUrlencodedData(authData))); + } + [Test] public void IsAuthenticationGrantedWithPairingReturnsFalse() { From 027965936050f03239662b208c847031c79f2ef3 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Mon, 23 Mar 2015 11:43:04 -0500 Subject: [PATCH 093/103] Fix typo in docs --- ToopherDotNet/ToopherDotNet.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 95c1f29..3a0dd30 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -103,7 +103,7 @@ public string GetAuthenticationUrl(string userName, string resetEmail="", string /// Unique name that identifies this user. This will be displayed to the user on their mobile device when they pair or authenticate. /// Email address that the user has access to. In case the user has lost or cannot access their mobile device, Toopher will send a reset email to this address. /// An optional Dictionary of extra parameters to provide to the API. - /// A string URL that can be used to retrieve the Pairing iFrame by the user's browser. + /// A string URL that can be used to retrieve the Pairing iFrame by the user's browser. public string GetUserManagementUrl(string userName, string resetEmail="", Dictionary extras = null) { NameValueCollection parameters = new NameValueCollection(); From 2765a0b983db5181cb447a0076078c5c3caf1a8f Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Mon, 23 Mar 2015 16:46:47 -0500 Subject: [PATCH 094/103] Cleanup text in demo --- ToopherDotNetDemo/ToopherDotNetDemo.cs | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/ToopherDotNetDemo/ToopherDotNetDemo.cs b/ToopherDotNetDemo/ToopherDotNetDemo.cs index c65d886..1b08160 100644 --- a/ToopherDotNetDemo/ToopherDotNetDemo.cs +++ b/ToopherDotNetDemo/ToopherDotNetDemo.cs @@ -12,12 +12,10 @@ class ToopherDotNetDemo public static void Main (string[] args) { - Console.WriteLine ("======================================"); - Console.WriteLine ("Library Usage Demo"); + Console.WriteLine (); + Console.WriteLine ("Toopher Library Demo"); Console.WriteLine ("======================================"); Console.WriteLine (""); - Console.WriteLine ("Setup Credentials"); - Console.WriteLine ("--------------------------------------"); string consumerKey = System.Environment.GetEnvironmentVariable ("TOOPHER_CONSUMER_KEY"); string consumerSecret = System.Environment.GetEnvironmentVariable ("TOOPHER_CONSUMER_SECRET"); if ((consumerKey == null) || (consumerSecret == null)) { @@ -91,6 +89,7 @@ public static void Main (string[] args) } while (true) { + Console.WriteLine (); Console.WriteLine ("Step 2: Authenticate log in"); Console.WriteLine ("--------------------------------------"); Console.Write (String.Format ("Enter a terminal name for this authentication request [\"{0}\"]: ", DEFAULT_TERMINAL_NAME)); @@ -131,7 +130,7 @@ public static void Main (string[] args) } } - Console.WriteLine ("Press return to authenticate again, or Ctrl-C to exit"); + Console.Write ("Press return to authenticate again, or Ctrl-C to exit."); Console.ReadLine (); } } From dfc0fdee645ae03cf05a591a2281c6aa0829a5a5 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Mon, 23 Mar 2015 17:42:56 -0500 Subject: [PATCH 095/103] Add js for ToopherIframe --- assets/js/toopher-web.js | 43 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 assets/js/toopher-web.js diff --git a/assets/js/toopher-web.js b/assets/js/toopher-web.js new file mode 100644 index 0000000..deb2209 --- /dev/null +++ b/assets/js/toopher-web.js @@ -0,0 +1,43 @@ +(function(window, $){ + var postToUrl = function (path, params, method){ + method = method || 'POST'; + var form = $('
').attr('method', method).attr('action', path); + for (var key in params){ + if (params.hasOwnProperty(key)){ + var hiddenField = $('').attr('type', 'hidden').attr('name', key).attr('value', params[key]); + form.append(hiddenField); + } + } + $('body').append(form); + form.submit(); + } + + var handleMessage = function(e){ + var msgData = JSON.parse(e.data); + if (msgData.status === 'toopher-api-complete'){ + var iframe = $('#toopher_iframe'); + var frameworkPostArgsJSON = iframe.attr('framework_post_args'); + var frameworkPostArgs = {}; + if(frameworkPostArgsJSON){ + frameworkPostArgs = $.parseJSON(frameworkPostArgsJSON); + } + var postData = $.extend({}, msgData.payload, frameworkPostArgs); + var toopherData = {'toopher_iframe_data': $.param(postData)}; + + if(iframe.attr('use_ajax_postback')){ + $.post(iframe.attr('toopher_postback'), toopherData) + .done(function(data){ + data = $.parseJSON(data); + }); + } else { + postToUrl(iframe.attr('toopher_postback'), toopherData, 'POST'); + } + } + } + + if (window.addEventListener) { + window.addEventListener('message', handleMessage, false); + } else { + window.attachEvent('onmessage', handleMessage); + } +})(window, jQuery); From 4857289af85eedfefe20fff5372e0180953b4a46 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 24 Mar 2015 16:59:30 -0500 Subject: [PATCH 096/103] Include baseUrl when creating new instance of ToopherApi --- ToopherDotNet/ToopherDotNet.cs | 2 +- ToopherDotNetTests/ToopherDotNetTests.cs | 1 - 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index 3a0dd30..ec486e8 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -182,7 +182,7 @@ public Object ProcessPostback(Dictionary parameters, string requ } else { ValidatePostback(toopherData, requestToken, extras); - ToopherApi api = new ToopherApi(consumerKey, consumerSecret); + ToopherApi api = new ToopherApi(consumerKey, consumerSecret, baseUrl); var resourceType = toopherData["resource_type"]; if (resourceType == "authentication_request") { diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index d302228..68f8e96 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -320,7 +320,6 @@ public void IsAuthenticationGrantedWithAuthenticationRequestGrantedAndPendingRet authData.Set("granted", "true"); authData.Set("pending", "true"); authData.Set("toopher_sig", "vmWBQCy8Py5PVkMZRppbCG7cm0w="); - Console.WriteLine("here's the test"); Assert.IsFalse(api.IsAuthenticationGranted(GetUrlencodedData(authData))); } From 6f066b6e15b66071e2f9b93dd31178d195feecfe Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Mar 2015 08:57:43 -0500 Subject: [PATCH 097/103] Add MIT license --- LICENSE.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 LICENSE.txt diff --git a/LICENSE.txt b/LICENSE.txt new file mode 100644 index 0000000..ac87d1c --- /dev/null +++ b/LICENSE.txt @@ -0,0 +1,7 @@ +Copyright (c) 2012 Toopher, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 70efc711677a11f1ed9a3d027304b275dd122f62 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Mar 2015 10:45:45 -0500 Subject: [PATCH 098/103] Trim extra comma from authorization header in OAuth lib --- ToopherDotNet/OAuth/OAuthRequest.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ToopherDotNet/OAuth/OAuthRequest.cs b/ToopherDotNet/OAuth/OAuthRequest.cs index 558f8f9..fa5bf6b 100644 --- a/ToopherDotNet/OAuth/OAuthRequest.cs +++ b/ToopherDotNet/OAuth/OAuthRequest.cs @@ -118,7 +118,7 @@ private string WriteAuthorizationHeader(WebParameterCollection parameters) } var authorization = sb.ToString(); - return authorization; + return authorization.TrimEnd(new char[]{ ',' }); } #endregion From 44fa5b8cb5cca339812c8a67eba29134d9bd4600 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Mar 2015 11:59:53 -0500 Subject: [PATCH 099/103] Add more tests to increase coverage --- ToopherDotNetTests/ToopherDotNetTests.cs | 111 ++++++++++++++++++++++- 1 file changed, 109 insertions(+), 2 deletions(-) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 68f8e96..7ebc513 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -58,6 +58,7 @@ public class WebClientMock : WebClientProxy { static public String LastRequestMethod { get; set; } static public NameValueCollection LastRequestData { get; set; } + static public String LastRequestUrl { get; set; } static public Exception ReturnException { get; set; } static public string ReturnValue { get; set; } static public string ReturnValueArray { get; set; } @@ -79,12 +80,14 @@ string doit() override public byte[] UploadValues(string requestUri, string method, NameValueCollection parameters) { + LastRequestUrl = requestUri; LastRequestMethod = "POST"; LastRequestData = parameters; return System.Text.Encoding.UTF8.GetBytes(doit()); } override public string DownloadString(string requestUri) { + LastRequestUrl = requestUri; LastRequestMethod = "GET"; LastRequestData = this.QueryString; return doit(); @@ -102,7 +105,7 @@ public virtual void Init() public ToopherApi GetToopherApi() { - return new ToopherApi("key", "secret", null, typeof(WebClientMock)); + return new ToopherApi("key", "secret", DEFAULT_BASE_URL, typeof(WebClientMock)); } } @@ -562,6 +565,7 @@ public void CreatePairingWithPhraseTest() var api = GetToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair("some user", "awkward turtle"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/pairings/create"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["pairing_phrase"], "awkward turtle"); Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "some user"); @@ -574,6 +578,7 @@ public void CreateSmsPairingTest() var api = GetToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair("some user", "555-555-5555"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/pairings/create/sms"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["phone_number"], "555-555-5555"); Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "some user"); @@ -586,6 +591,7 @@ public void CreateQrPairingTest() var api = GetToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair("some user"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/pairings/create/qr"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "some user"); Assert.AreEqual(pairing.id, "1"); @@ -597,6 +603,7 @@ public void ArbitraryParametersOnPairTest() var api = GetToopherApi(); WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.Pair("some user", "awkward turtle", extras: new Dictionary(){{"test_param", "42"}}); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/pairings/create"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["test_param"], "42"); Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "some user"); @@ -611,6 +618,7 @@ public void AuthenticateWithPairingIdTest() string pairingId = Guid.NewGuid().ToString(); WebClientMock.ReturnValue = @"{""id"":""" + pairingId + @""", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"",""requester_specified_id"":""requesterSpecifiedId"",""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}}"; AuthenticationRequest auth = api.Authenticate(pairingId, "test terminal"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/authentication_requests/initiate"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["pairing_id"], pairingId); Assert.AreEqual(WebClientMock.LastRequestData["terminal_name"], "test terminal"); @@ -623,6 +631,7 @@ public void AuthenticateWithUsernameAndExtrasTest() var api = GetToopherApi(); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.Authenticate("some other user", requesterSpecifiedId: "requester specified id", extras: new Dictionary(){{ "random_key" , "42" }}); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/authentication_requests/initiate"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "some other user"); Assert.AreEqual(WebClientMock.LastRequestData["requester_specified_terminal_id"], "requester specified id"); @@ -631,11 +640,12 @@ public void AuthenticateWithUsernameAndExtrasTest() } [Test] - public void ArbitraryParamtersOnAuthenticateTest() + public void ArbitraryParametersOnAuthenticateTest() { var api = GetToopherApi(); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.Authenticate("1", "test terminal", extras: new Dictionary(){{ "test_param", "42" }}); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/authentication_requests/initiate"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["test_param"], "42"); Assert.AreEqual(auth.id, "1"); @@ -690,6 +700,7 @@ public void AdvancedPairingsGetByIdTest() WebClientMock.ReturnValue = PAIRING_RESPONSE; Pairing pairing = api.advanced.pairings.GetById("1"); Assert.IsInstanceOf(pairing); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/pairings/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual(pairing.id, "1"); Assert.AreEqual(pairing.user.name, "some user"); @@ -703,6 +714,7 @@ public void AccessArbitraryKeysInAdvancedPairingsGetByIdTest() var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""enabled"":true, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""random_key"":""84""}"; Pairing pairing = api.advanced.pairings.GetById("1"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/pairings/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual(pairing.id, "1"); Assert.AreEqual(pairing.user.name, "some user"); @@ -717,6 +729,7 @@ public void AdvancedAuthenticationRequestsGetByIdTest() var api = GetToopherApi(); WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = api.advanced.authenticationRequests.GetById("1"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/authentication_requests/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual(auth.id, "1"); Assert.IsFalse(auth.pending); @@ -732,6 +745,7 @@ public void AccessArbitraryKeysInAdvancedAuthenticationRequestsGetByIdTest() var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""pending"":false, ""granted"":true, ""automated"":false, ""reason_code"":1, ""reason"":""its a test"", ""terminal"":{""id"":""1"", ""name"":""test terminal"", ""requester_specified_id"": ""requesterSpecifiedId"", ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}}, ""user"":{""id"":""1"",""name"":""some user"", ""toopher_authentication_enabled"":true}, ""action"": {""id"":""1"", ""name"":""actionName""}, ""random_key"":""84""}"; AuthenticationRequest auth = api.advanced.authenticationRequests.GetById("1"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/authentication_requests/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual(auth.id, "1"); Assert.IsFalse(auth.pending); @@ -749,6 +763,7 @@ public void AdvancedUsersGetByIdTest() WebClientMock.ReturnValue = USER_RESPONSE; User user = api.advanced.users.GetById("1"); Assert.IsInstanceOf(user); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/users/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual(user.id, "1"); Assert.AreEqual(user.name, "userName"); @@ -761,6 +776,7 @@ public void AccessArbitraryKeysInAdvancedUsersGetByIdTest() var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true, ""random_key"":""84""}"; User user = api.advanced.users.GetById("1"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/users/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual(user.id, "1"); Assert.AreEqual(user.name, "userName"); @@ -775,7 +791,9 @@ public void AdvancedUsersGetByNameTest() WebClientMock.ReturnValueArray = @"[{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}]"; WebClientMock.ReturnValue = USER_RESPONSE; User user = api.advanced.users.GetByName("userName"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/users"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); + Assert.AreEqual(WebClientMock.LastRequestData["name"], "userName"); Assert.AreEqual(user.id, "1"); Assert.AreEqual(user.name, "userName"); Assert.IsTrue(user.toopherAuthenticationEnabled); @@ -788,7 +806,9 @@ public void AdvancedUsersCreateTest() WebClientMock.ReturnValue = USER_RESPONSE; User user = api.advanced.users.Create("userName"); Assert.IsInstanceOf(user); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/users/create"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["name"], "userName"); Assert.AreEqual(user.id, "1"); Assert.AreEqual(user.name, "userName"); Assert.IsTrue(user.toopherAuthenticationEnabled); @@ -803,8 +823,10 @@ public void AdvancedUsersCreateWithParamsTest() parameters.Add("foo", "bar"); User user = api.advanced.users.Create("userName", parameters); Assert.IsInstanceOf(user); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/users/create"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["foo"], "bar"); + Assert.AreEqual(WebClientMock.LastRequestData["name"], "userName"); Assert.AreEqual(user.id, "1"); Assert.AreEqual(user.name, "userName"); Assert.IsTrue(user.toopherAuthenticationEnabled); @@ -817,6 +839,7 @@ public void AdvancedUserTerminalsGetByIdTest() WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; UserTerminal userTerminal = api.advanced.userTerminals.GetById("1"); Assert.IsInstanceOf(userTerminal); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/user_terminals/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual(userTerminal.id, "1"); Assert.AreEqual(userTerminal.name, "userTerminalName"); @@ -830,6 +853,7 @@ public void AccessArbitraryKeysInAdvancedUserTerminalsGetByIdTest() var api = GetToopherApi(); WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userTerminalName"", ""requester_specified_id"":""requesterSpecifiedId"",""random_key"":""84"", ""user"":{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":true}}"; UserTerminal userTerminal = api.advanced.userTerminals.GetById("1"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/user_terminals/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual(userTerminal.id, "1"); Assert.AreEqual(userTerminal.name, "userTerminalName"); @@ -843,7 +867,11 @@ public void AdvancedUserTerminalsCreateTest() var api = GetToopherApi(); WebClientMock.ReturnValue = USER_TERMINAL_RESPONSE; UserTerminal userTerminal = api.advanced.userTerminals.Create("userName", "userTerminalName", "requesterSpecifiedId"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/user_terminals/create"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "userName"); + Assert.AreEqual(WebClientMock.LastRequestData["terminal_name"], "userTerminalName"); + Assert.AreEqual(WebClientMock.LastRequestData["requester_specified_id"], "requesterSpecifiedId"); Assert.AreEqual(userTerminal.id, "1"); Assert.AreEqual(userTerminal.name, "userTerminalName"); Assert.AreEqual(userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); @@ -858,8 +886,12 @@ public void AdvancedUserTerminalsCreateWithParamsTest() NameValueCollection parameters = new NameValueCollection(); parameters.Add("foo", "bar"); UserTerminal userTerminal = api.advanced.userTerminals.Create("userName", "userTerminalName", "requesterSpecifiedId", parameters); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/user_terminals/create"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["foo"], "bar"); + Assert.AreEqual(WebClientMock.LastRequestData["user_name"], "userName"); + Assert.AreEqual(WebClientMock.LastRequestData["terminal_name"], "userTerminalName"); + Assert.AreEqual(WebClientMock.LastRequestData["requester_specified_id"], "requesterSpecifiedId"); Assert.AreEqual(userTerminal.id, "1"); Assert.AreEqual(userTerminal.name, "userTerminalName"); Assert.AreEqual(userTerminal.requesterSpecifiedId, "requesterSpecifiedId"); @@ -943,6 +975,7 @@ public void PairingRefreshFromServerTest() Pairing pairing = new Pairing(PAIRING_DICT, api); pairing.RefreshFromServer(); Assert.IsInstanceOf(pairing); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/pairings/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual(pairing.id, "1"); Assert.AreEqual(pairing.user.name, "userNameChanged"); @@ -958,7 +991,22 @@ public void PairingGetResetLinkTest() WebClientMock.ReturnValue = @"{""url"":""" + link + @"""}"; Pairing pairing = new Pairing(PAIRING_DICT, api); string returnedLink = pairing.GetResetLink(); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/pairings/1/generate_reset_link"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(returnedLink, link); + } + + [Test] + public void PairingGetResetLinkWithExtrasTest() + { + var api = GetToopherApi(); + string link = "http://api.toopher.test/v1/pairings/1/reset?reset_authorization=abcde"; + WebClientMock.ReturnValue = @"{""url"":""" + link + @"""}"; + Pairing pairing = new Pairing(PAIRING_DICT, api); + string returnedLink = pairing.GetResetLink(new Dictionary(){{ "foo" , "bar" }}); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/pairings/1/generate_reset_link"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["foo"], "bar"); Assert.AreEqual(returnedLink, link); } @@ -969,8 +1017,22 @@ public void PairingEmailResetLinkTest() WebClientMock.ReturnValue = @"{}"; Pairing pairing = new Pairing(PAIRING_DICT, api); pairing.EmailResetLink("test@test.com"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/pairings/1/send_reset_link"); + Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); + Assert.AreEqual(WebClientMock.LastRequestData["reset_email"], "test@test.com"); + } + + [Test] + public void PairingEmailResetLinkWithExtrasTest() + { + var api = GetToopherApi(); + WebClientMock.ReturnValue = @"{}"; + Pairing pairing = new Pairing(PAIRING_DICT, api); + pairing.EmailResetLink("test@test.com", new Dictionary(){{ "foo" , "bar" }}); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/pairings/1/send_reset_link"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["reset_email"], "test@test.com"); + Assert.AreEqual(WebClientMock.LastRequestData["foo"], "bar"); } [Test] @@ -980,6 +1042,7 @@ public void PairingGetQrCodeImage() WebClientMock.ReturnValue = @"{}"; Pairing pairing = new Pairing(PAIRING_DICT, api); pairing.GetQrCodeImage(); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/qr/pairings/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); } } @@ -1019,6 +1082,7 @@ public void AuthenticationRequestRefreshFromServerTest() AuthenticationRequest auth = new AuthenticationRequest(AUTH_REQUEST_DICT, api); auth.RefreshFromServer(); Assert.IsInstanceOf(auth); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/authentication_requests/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.IsTrue(auth.pending); Assert.AreEqual(auth.reasonCode, 2); @@ -1034,6 +1098,7 @@ public void AuthenticationRequestGrantWithOtpTest() WebClientMock.ReturnValue = AUTH_REQUEST_RESPONSE; AuthenticationRequest auth = new AuthenticationRequest(AUTH_REQUEST_DICT, api); auth.GrantWithOtp("123456"); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/authentication_requests/1/otp_auth"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["otp"], "123456"); Assert.AreEqual(auth.id, "1"); @@ -1065,6 +1130,7 @@ public void UserRefreshFromServerTest() User user = new User(USER_DICT, api); user.RefreshFromServer(); Assert.IsInstanceOf(user); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/users/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual(user.id, "1"); Assert.AreEqual(user.name, "userNameChanged"); @@ -1078,6 +1144,7 @@ public void UserEnableToopherAuthentication() WebClientMock.ReturnValue = USER_RESPONSE; User user = new User(USER_DICT, api); user.EnableToopherAuthentication(); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/users/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["toopher_authentication_enabled"], "true"); Assert.AreEqual(user.id, "1"); @@ -1091,6 +1158,7 @@ public void UserDisableToopherAuthentication() WebClientMock.ReturnValue = @"{""id"":""1"", ""name"":""userName"", ""toopher_authentication_enabled"":false}"; User user = new User(USER_DICT, api); user.DisableToopherAuthentication(); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/users/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["toopher_authentication_enabled"], "false"); Assert.AreEqual(user.id, "1"); @@ -1104,6 +1172,7 @@ public void UserResetTest() WebClientMock.ReturnValue = USER_RESPONSE; User user = new User(USER_DICT, api); user.Reset(); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/users/reset"); Assert.AreEqual(WebClientMock.LastRequestMethod, "POST"); Assert.AreEqual(WebClientMock.LastRequestData["name"], "userName"); } @@ -1136,6 +1205,7 @@ public void UserTerminalRefreshFromServerTest() UserTerminal userTerminal = new UserTerminal(USER_TERMINAL_DICT, api); userTerminal.RefreshFromServer(); Assert.IsInstanceOf(userTerminal); + Assert.AreEqual(WebClientMock.LastRequestUrl, "https://api.toopher.test/v1/user_terminals/1"); Assert.AreEqual(WebClientMock.LastRequestMethod, "GET"); Assert.AreEqual(userTerminal.id, "1"); Assert.AreEqual(userTerminal.name, "userTerminalNameChanged"); @@ -1156,4 +1226,41 @@ public void CreateActionTest() Assert.AreEqual(action.name, "actionName"); } } + + [TestFixture()] + public class ToopherHelperTests : TestBase + { + [Test] + public void AddExtrasToCollectionTest() + { + NameValueCollection parameters = new NameValueCollection(); + parameters.Add("foo", "bar"); + Dictionary extras = new Dictionary(){{"hello", "world"}}; + parameters = ToopherHelper.AddExtrasToCollection(parameters, extras); + Assert.AreEqual(parameters["foo"], "bar"); + Assert.AreEqual(parameters["hello"], "world"); + } + } + + [TestFixture()] + public class ErrorCodeTests : TestBase + { + [Test] + public void UserDisabledErrorCodeTest() + { + Assert.AreEqual(UserDisabledError.ERROR_CODE, 704); + } + + [Test] + public void UserUnknownErrorCodeTest() + { + Assert.AreEqual(UserUnknownError.ERROR_CODE, 705); + } + + [Test] + public void TerminalUnknownErrorCodeTest() + { + Assert.AreEqual(TerminalUnknownError.ERROR_CODE, 706); + } + } } From e38e8e8d0697399448d37f6833c81fe09c5e4f4b Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Wed, 25 Mar 2015 12:09:04 -0500 Subject: [PATCH 100/103] Cleanup ToopherIframe and ToopherApi --- ToopherDotNet/ToopherDotNet.cs | 211 ++++++++++++--------------------- 1 file changed, 77 insertions(+), 134 deletions(-) diff --git a/ToopherDotNet/ToopherDotNet.cs b/ToopherDotNet/ToopherDotNet.cs index ec486e8..7d68bee 100644 --- a/ToopherDotNet/ToopherDotNet.cs +++ b/ToopherDotNet/ToopherDotNet.cs @@ -49,13 +49,7 @@ public ToopherIframe(string consumerKey, string consumerSecret, string baseUrl = { this.consumerKey = consumerKey; this.consumerSecret = consumerSecret; - if (baseUrl != null) - { - this.baseUrl = baseUrl; - } else - { - this.baseUrl = DEFAULT_BASE_URL; - } + this.baseUrl = baseUrl ?? DEFAULT_BASE_URL; } /// @@ -74,7 +68,7 @@ public string GetAuthenticationUrl(string userName, string resetEmail="", string long ttl; if (extras != null && extras.ContainsKey("ttl")) { - ttl = Int32.Parse(extras["ttl"]); + ttl = Int64.Parse(extras["ttl"]); extras.Remove("ttl"); } else { @@ -86,14 +80,7 @@ public string GetAuthenticationUrl(string userName, string resetEmail="", string parameters.Add("session_token", requestToken); parameters.Add("action_name", actionName); parameters.Add("requester_metadata", requesterMetadata); - - if (extras != null) - { - foreach (KeyValuePair kvp in extras) - { - parameters.Add(kvp.Key, kvp.Value); - } - } + parameters = ToopherHelper.AddExtrasToCollection(parameters, extras); return GetOauthUrl(baseUrl + "web/authenticate", parameters, ttl); } @@ -110,7 +97,7 @@ public string GetUserManagementUrl(string userName, string resetEmail="", Dictio long ttl; if (extras != null && extras.ContainsKey("ttl")) { - ttl = Int32.Parse(extras["ttl"]); + ttl = Int64.Parse(extras["ttl"]); extras.Remove("ttl"); } else { @@ -119,14 +106,7 @@ public string GetUserManagementUrl(string userName, string resetEmail="", Dictio parameters.Add("username", userName); parameters.Add("reset_email", resetEmail); - - if (extras != null) - { - foreach (KeyValuePair kvp in extras) - { - parameters.Add(kvp.Key, kvp.Value); - } - } + parameters = ToopherHelper.AddExtrasToCollection(parameters, extras); return GetOauthUrl(baseUrl + "web/manage_user", parameters, ttl); } @@ -142,13 +122,7 @@ public bool IsAuthenticationGranted(Dictionary parameters, strin try { AuthenticationRequest authenticationRequest = (AuthenticationRequest) ProcessPostback(parameters, requestToken, extras); - if (!authenticationRequest.pending && authenticationRequest.granted) - { - return true; - } else - { - return false; - } + return !authenticationRequest.pending && authenticationRequest.granted; } catch (UserDisabledError) { return true; @@ -171,31 +145,28 @@ public Object ProcessPostback(Dictionary parameters, string requ if (toopherData.ContainsKey("error_code")) { var errorCode = Int32.Parse(toopherData["error_code"]); - var errorMessage = toopherData["error_message"]; if (errorCode == UserDisabledError.ERROR_CODE) { throw new UserDisabledError(); } else { - throw new RequestError(errorMessage); + throw new RequestError(toopherData["error_message"]); } } else { ValidatePostback(toopherData, requestToken, extras); ToopherApi api = new ToopherApi(consumerKey, consumerSecret, baseUrl); - var resourceType = toopherData["resource_type"]; - if (resourceType == "authentication_request") - { - return new AuthenticationRequest(CreateAuthenticationRequestDict(toopherData), api); - } else if (resourceType == "pairing") - { - return new Pairing(CreatePairingDict(toopherData), api); - } else if (resourceType == "requester_user") - { - return new User(CreateUserDict(toopherData), api); - } else + + switch (toopherData["resource_type"]) { - throw new RequestError ("The postback resource type is not valid: " + resourceType); + case "authentication_request": + return new AuthenticationRequest(CreateAuthenticationRequestDict(toopherData), api); + case "pairing": + return new Pairing(CreatePairingDict(toopherData), api); + case "requester_user": + return new User(CreateUserDict(toopherData), api); + default: + throw new RequestError("The postback resource type is not valid: " + toopherData["resource_type"]); } } } @@ -243,8 +214,8 @@ private void VerifySessionToken(string sessionToken, string requestToken) private void CheckIfSignatureIsExpired(string timestamp, Dictionary extras) { - var ttl = (extras != null && extras.ContainsKey("ttl")) ? Int32.Parse(extras["ttl"]) : DEFAULT_TTL; - var ttlValid = (int)(GetUnixEpochTimeInSeconds() - ttl) < Int32.Parse(timestamp); + var ttl = (extras != null && extras.ContainsKey("ttl")) ? Int64.Parse(extras["ttl"]) : DEFAULT_TTL; + var ttlValid = GetUnixEpochTimeInSeconds() - ttl < Int64.Parse(timestamp); if (!ttlValid) { throw new SignatureValidationError("TTL Expired"); @@ -338,19 +309,19 @@ private string GetOauthUrl(string url, NameValueCollection parameters, long ttl) return url + "?" + requestParams + "&" + oauthParams; } - private static int GetUnixEpochTimeInSeconds() + private static long GetUnixEpochTimeInSeconds() { TimeSpan t = (GetDate() - new DateTime(1970, 1, 1)); - return (int)t.TotalSeconds; + return (long)t.TotalSeconds; } private static string Signature(string secret, Dictionary data) { - Dictionary sortedData = data.OrderBy(x => x.Key).ToDictionary(x => x.Key, x => OAuthTools.UrlEncodeStrict(x.Value).Replace("%20", "+")); - string joinedString = string.Join("&", (sortedData.Select(d => d.Key + "=" + d.Value).ToArray())); + NameValueCollection parameters = ToopherHelper.AddExtrasToCollection(new NameValueCollection(), data); + string urlEncodedData = UrlEncodeParameters(parameters); byte[] keyBytes = Encoding.UTF8.GetBytes(secret); - byte[] messageBytes = Encoding.UTF8.GetBytes(joinedString); + byte[] messageBytes = Encoding.UTF8.GetBytes(urlEncodedData); using (var hmac = new HMACSHA1(keyBytes)) { @@ -364,7 +335,7 @@ private static string UrlEncodeParameters(NameValueCollection parameters) WebParameterCollection collection = new WebParameterCollection(parameters); foreach (var parameter in collection) { - parameter.Value = OAuthTools.UrlEncodeStrict(parameter.Value).Replace("%20", "+"); + parameter.Value = Uri.EscapeDataString(parameter.Value).Replace("%20", "+"); } collection.Sort((x, y) => x.Name.Equals(y.Name) ? x.Value.CompareTo(y.Value) : x.Name.CompareTo(y.Name)); return OAuthTools.Concatenate(collection, "=", "&"); @@ -418,20 +389,8 @@ public ToopherApi(string consumerKey, string consumerSecret, string baseUrl = nu this.advanced = new ToopherApi.AdvancedApiUsageFactory(this); this.consumerKey = consumerKey; this.consumerSecret = consumerSecret; - if (baseUrl != null) - { - this.baseUrl = baseUrl; - } else - { - this.baseUrl = ToopherApi.DEFAULT_BASE_URL; - } - if (webClientProxyType != null) - { - this.webClientProxyType = webClientProxyType; - } else - { - this.webClientProxyType = typeof(WebClientProxy); - } + this.baseUrl = baseUrl ?? DEFAULT_BASE_URL; + this.webClientProxyType = webClientProxyType ?? typeof(WebClientProxy); } /// @@ -467,14 +426,7 @@ public Pairing Pair(string userName, string pairingPhraseOrNum = null, Dictionar endpoint = "pairings/create/qr"; } - if (extras != null) - { - foreach (KeyValuePair kvp in extras) - { - parameters.Add(kvp.Key, kvp.Value); - } - } - + parameters = ToopherHelper.AddExtrasToCollection(parameters, extras); var json = advanced.raw.post(endpoint, parameters); return new Pairing(json, this); } @@ -515,14 +467,7 @@ public AuthenticationRequest Authenticate(string pairingIdOrUsername, string ter parameters.Add("action_name", actionName); } - if (extras != null) - { - foreach (KeyValuePair kvp in extras) - { - parameters.Add(kvp.Key, kvp.Value); - } - } - + parameters = ToopherHelper.AddExtrasToCollection(parameters, extras); var json = advanced.raw.post(endpoint, parameters); return new AuthenticationRequest(json, this); } @@ -571,7 +516,7 @@ public Pairings(ToopherApi toopherApi) : base(toopherApi) /// A object. public Pairing GetById(string pairingId) { - string endpoint = string.Format("pairings/{0}", pairingId); + string endpoint = "pairings/" + pairingId; var json = api.advanced.raw.get(endpoint); return new Pairing(json, api); } @@ -590,7 +535,7 @@ public AuthenticationRequests(ToopherApi toopherApi) : base(toopherApi) /// A object. public AuthenticationRequest GetById(string authenticationRequestId) { - string endpoint = string.Format("authentication_requests/{0}", authenticationRequestId); + string endpoint = "authentication_requests/" + authenticationRequestId; var json = api.advanced.raw.get(endpoint); return new AuthenticationRequest(json, api); } @@ -609,7 +554,7 @@ public Users(ToopherApi toopherApi) : base(toopherApi) /// A object. public User GetById(string userId) { - string endpoint = string.Format("users/{0}", userId); + string endpoint = "users/" + userId; var json = api.advanced.raw.get(endpoint); return new User(json, api); } @@ -629,11 +574,11 @@ public User GetByName(string userName) if (json.Count() > 1) { - throw new RequestError(string.Format("More than one user with name {0}", userName)); + throw new RequestError("More than one user with name " + userName); } if (json.Count() == 0) { - throw new RequestError(string.Format("No users with name {0}", userName)); + throw new RequestError("No users with name " + userName); } return new User((JsonObject)json[0], api); } @@ -670,7 +615,7 @@ public UserTerminals(ToopherApi toopherApi) : base(toopherApi) /// A object. public UserTerminal GetById(string userTerminalId) { - string endpoint = string.Format("user_terminals/{0}", userTerminalId); + string endpoint = "user_terminals/" + userTerminalId; var json = api.advanced.raw.get(endpoint); return new UserTerminal(json, api); } @@ -732,8 +677,6 @@ private object request(string method, string endpoint, NameValueCollection param client.Method = method; string auth = client.GetAuthorizationHeader(parameters); - // FIXME: OAuth library puts extraneous comma at end, workaround: remove it if present - auth = auth.TrimEnd(new char[]{ ',' }); using (WebClientProxy wClient = (WebClientProxy)Activator.CreateInstance(api.webClientProxyType)) { @@ -816,26 +759,23 @@ public JsonObject post(string endpoint, NameValueCollection parameters = null) private void parseRequestError(JsonObject err) { - long errCode = (long)err["error_code"]; string errMessage = (string)err["error_message"]; - if (errCode == UserDisabledError.ERROR_CODE) - { - throw new UserDisabledError(); - } else if (errCode == UserUnknownError.ERROR_CODE) - { - throw new UserUnknownError(); - } else if (errCode == TerminalUnknownError.ERROR_CODE) - { - throw new TerminalUnknownError(); - } else + switch ((long)err["error_code"]) { - if (errMessage.ToLower().Contains("pairing has been deactivated") || errMessage.ToLower().Contains("pairing has not been authorized")) - { - throw new PairingDeactivatedError(); - } else - { - throw new RequestError(errMessage); - } + case UserDisabledError.ERROR_CODE: + throw new UserDisabledError(); + case UserUnknownError.ERROR_CODE: + throw new UserUnknownError(); + case TerminalUnknownError.ERROR_CODE: + throw new TerminalUnknownError(); + default: + if (errMessage.ToLower().Contains("pairing has been deactivated") || errMessage.ToLower().Contains("pairing has not been authorized")) + { + throw new PairingDeactivatedError(); + } else + { + throw new RequestError(errMessage); + } } } } @@ -901,7 +841,7 @@ public Pairing(IDictionary response, ToopherApi toopherApi) /// public void RefreshFromServer() { - string endpoint = string.Format("pairings/{0}", id); + string endpoint = "pairings/" + id; var json = api.advanced.raw.get(endpoint); Update(json); } @@ -915,13 +855,7 @@ public string GetResetLink(Dictionary extras = null) { string endpoint = string.Format("pairings/{0}/generate_reset_link", id); NameValueCollection parameters = new NameValueCollection(); - if (extras != null) - { - foreach (KeyValuePair kvp in extras) - { - parameters.Add(kvp.Key, kvp.Value); - } - } + parameters = ToopherHelper.AddExtrasToCollection(parameters, extras); var json = api.advanced.raw.post(endpoint, parameters); return (string)json["url"]; } @@ -936,13 +870,7 @@ public void EmailResetLink(string email, Dictionary extras = nul string endpoint = string.Format("pairings/{0}/send_reset_link", id); NameValueCollection parameters = new NameValueCollection(); parameters.Add("reset_email", email); - if (extras != null) - { - foreach (KeyValuePair kvp in extras) - { - parameters.Add(kvp.Key, kvp.Value); - } - } + parameters = ToopherHelper.AddExtrasToCollection(parameters, extras); api.advanced.raw.post(endpoint, parameters); } @@ -952,7 +880,7 @@ public void EmailResetLink(string email, Dictionary extras = nul /// QR code image stored as a byte[]. public byte[] GetQrCodeImage() { - string endpoint = string.Format("qr/pairings/{0}", id); + string endpoint = "qr/pairings/" + id; var result = api.advanced.raw.get(endpoint); return System.Text.Encoding.UTF8.GetBytes(result.ToString()); } @@ -1052,7 +980,7 @@ public AuthenticationRequest(IDictionary response, ToopherApi to /// public void RefreshFromServer() { - string endpoint = string.Format("authentication_requests/{0}", id); + string endpoint = "authentication_requests/" + id; var json = api.advanced.raw.get(endpoint); Update(json); } @@ -1146,7 +1074,7 @@ public User(IDictionary response, ToopherApi toopherApi) ///
public void RefreshFromServer() { - string endpoint = string.Format("users/{0}", id); + string endpoint = "users/" + id; var json = api.advanced.raw.get(endpoint); Update(json); } @@ -1173,7 +1101,7 @@ public void Update(IDictionary response) ///
public void EnableToopherAuthentication() { - string endpoint = string.Format("users/{0}", id); + string endpoint = "users/" + id; NameValueCollection parameters = new NameValueCollection(); parameters.Add("toopher_authentication_enabled", "true"); var json = api.advanced.raw.post(endpoint, parameters); @@ -1187,7 +1115,7 @@ public void EnableToopherAuthentication() ///
public void DisableToopherAuthentication() { - string endpoint = string.Format("users/{0}", id); + string endpoint = "users/" + id; NameValueCollection parameters = new NameValueCollection(); parameters.Add("toopher_authentication_enabled", "false"); var json = api.advanced.raw.post(endpoint, parameters); @@ -1264,7 +1192,7 @@ public UserTerminal(IDictionary response, ToopherApi toopherApi) ///
public void RefreshFromServer() { - string endpoint = string.Format("user_terminals/{0}", id); + string endpoint = "user_terminals/" + id; var json = api.advanced.raw.get(endpoint); Update(json); } @@ -1346,6 +1274,21 @@ public void Update(IDictionary response) } } + public static class ToopherHelper + { + public static NameValueCollection AddExtrasToCollection(NameValueCollection parameters, Dictionary extras = null) + { + if (extras != null) + { + foreach (KeyValuePair kvp in extras) + { + parameters.Add(kvp.Key, kvp.Value); + } + } + return parameters; + } + } + // An exception class used to indicate an error in a request public class RequestError : System.ApplicationException { @@ -1359,7 +1302,7 @@ public RequestError(string message, System.Exception inner) : base(message, inne ///
public class UserDisabledError : RequestError { - static public int ERROR_CODE = 704; + public const int ERROR_CODE = 704; } /// @@ -1369,7 +1312,7 @@ public class UserDisabledError : RequestError /// public class UserUnknownError : RequestError { - static public int ERROR_CODE = 705; + public const int ERROR_CODE = 705; } /// @@ -1378,7 +1321,7 @@ public class UserUnknownError : RequestError /// public class TerminalUnknownError : RequestError { - static public int ERROR_CODE = 706; + public const int ERROR_CODE = 706; } /// From 4aca72c5f4d6b0aadffb9278d3144b774e79aca0 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Thu, 26 Mar 2015 16:11:24 -0500 Subject: [PATCH 101/103] Add license to README --- README.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/README.md b/README.md index 2746184..c1795cc 100644 --- a/README.md +++ b/README.md @@ -63,3 +63,6 @@ $ xbuild $ mono ToopherDotNetDemo/bin/Debug/ToopherDotNetDemo.exe ``` To avoid being prompted for your Toopher API key and secret, you can define them in the $TOOPHER_CONSUMER_KEY and $TOOPHER_CONSUMER_SECRET environment variables + +#### License +ToopherDotNet is licensed under the MIT License. See LICENSE.txt for the full license text. From 4247336bba6944ba4c2e551deb68a082234154c5 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Mon, 30 Mar 2015 17:05:53 -0500 Subject: [PATCH 102/103] Add ProcessPostback test for keys with empty values --- ToopherDotNetTests/ToopherDotNetTests.cs | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/ToopherDotNetTests/ToopherDotNetTests.cs b/ToopherDotNetTests/ToopherDotNetTests.cs index 7ebc513..10a4c77 100644 --- a/ToopherDotNetTests/ToopherDotNetTests.cs +++ b/ToopherDotNetTests/ToopherDotNetTests.cs @@ -438,6 +438,18 @@ public void ProcessPostbackWithoutRequestTokenReturnsAuthenticationRequest() Assert.IsInstanceOf(authRequest); } + [Test] + public void ProcessPostbackWithKeyWithEmptyValuesReturnsAuthenticationRequest() + { + ToopherIframe.SetDateOverride(TEST_DATE); + var api = GetToopherIframeApi(); + var authData = GetAuthenticationRequestData(); + authData.Set("requester_metadata", ""); + authData.Set("toopher_sig", "2CQouLu8dL3OA8N/mgHK6eeYHm4="); + var authRequest = api.ProcessPostback(GetUrlencodedData(authData)); + Assert.IsInstanceOf(authRequest); + } + [Test] public void ProcessPostbackWithBadSignatureThrowsError() { From 2278ac4a5cc79980c8987306d919fdfe7dc779d2 Mon Sep 17 00:00:00 2001 From: Grace Yim Date: Tue, 31 Mar 2015 10:22:40 -0500 Subject: [PATCH 103/103] Simplify README and add iframe info --- CONTRIBUTING.md | 22 ---------- README.md | 110 ++++++++++++++++++++++++++++++++---------------- 2 files changed, 73 insertions(+), 59 deletions(-) delete mode 100644 CONTRIBUTING.md diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index aa4d328..0000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,22 +0,0 @@ -# ToopherApi DotNet Client - -#### .NET Framework Version ->=4.5 - -#### C# Version ->=5.0 - -#### Installing Dependencies -This library was developed on Windows and OS X. To run .NET on OS X, we use [Mono](http://www.mono-project.com/). - -To install Mono with Homebrew run: -```shell -$ brew install mono -``` - -#### Tests -To test, run: -```shell -$ xbuild -$ nunit-console ./ToopherDotNetTests/bin/Debug/ToopherDotNetTests.dll -exclude Integration,NotWorkingOnMono -``` diff --git a/README.md b/README.md index c1795cc..2f52c8d 100644 --- a/README.md +++ b/README.md @@ -1,29 +1,20 @@ -#ToopherDotNet +#ToopherDotNet [![Build Status](https://travis-ci.org/toopher/toopher-dotnet.png?branch=master)](https://travis-ci.org/toopher/toopher-dotnet) -[![Build -Status](https://travis-ci.org/toopher/toopher-dotnet.png?branch=master)](https://travis-ci.org/toopher/toopher-dotnet) - -#### Introduction ToopherDotNet is a Toopher API library that simplifies the task of interfacing with the Toopher API from DotNet programs. It does not depend on any external libraries, and preconfigures the required OAuth and JSON functionality so you can focus on just using the API. -#### Learn the Toopher API -Make sure you visit (http://dev.toopher.com) to get acquainted with the Toopher API fundamentals. The documentation there will tell you the details about the operations this API wrapper library provides. - -#### OAuth Authentication - -The first step to accessing the Toopher API is to sign up for an account at the development portal (http://dev.toopher.com) and create a "requester". When that process is complete, your requester is issued OAuth 1.0a credentials in the form of a consumer key and secret. Your key is used to identify your quester when Toopher interacts with your customers, and the secret is used to sign each request so that we know it is generated by you. This library properly formats each request with your credentials automatically. +### .NET Framework Version +\>=4.5 -#### The Toopher Two-Step -Interacting with the Toopher web service involves two steps: pairing, and authenticating. +### C# Version +\>=5.0 -##### Pair -Before you can enhance your website's actions with Toopher, your customers will need to pair their phone's Toopher app with your website. To do this, they generate a unique, nonsensical "pairing phrase" from within the app on their phone. You will need to prompt them for a pairing phrase as part of the Toopher enrollment process. Once you have a pairing phrase, just send it to the Toopher API along with your requester credentials and we'll return a pairing ID that you can use whenever you want to authenticate an action for that user. +### Documentation +Make sure you visit [https://dev.toopher.com](https://dev.toopher.com) to get acquainted with the Toopher API fundamentals. The documentation there will tell you the details about the operations this API wrapper library provides. -##### Authenticate -You have complete control over what actions you want to authenticate using Toopher (for example: logging in, changing account information, making a purchase, etc.). Just send us the user's pairing ID, a name for the terminal they're using, and a description of the action they're trying to perform and we'll make sure they actually want it to happen. +## ToopherApi Workflow -#### Librarified -This library makes it super simple to do the Toopher two-step. Check it out: +### Step 1: Pair +Before you can enhance your website's actions with Toopher, your customers will need to pair their mobile device's Toopher app with your website. To do this, they generate a unique pairing phrase from within the app on their mobile device. You will need to prompt them for a pairing phrase as part of the Toopher enrollment process. Once you have a pairing phrase, just send it to the Toopher API along with your requester credentials and we'll return a pairing ID that you can use whenever you want to authenticate an action for that user. ```csharp using Toopher; @@ -31,38 +22,83 @@ using Toopher; // Create an API object using your credentials ToopherApi api = new ToopherApi("", ""); -// Step 1 - Pair with their phone's Toopher app -// With pairing phrase +// Step 1 - Pair with their mobile device's Toopher app Pairing pairing = api.Pair("username@yourservice.com", "pairing phrase"); -// With SMS -Pairing pairing = api.Pair("username@yourservice.com", "555-555-5555"); -// With QR code -Pairing pairing = api.Pair("username@yourservice.com"); +``` + +### Step 2: Authenticate +You have complete control over what actions you want to authenticate using Toopher (logging in, changing account information, making a purchase, etc.). Just send us the username or pairing ID and we'll make sure they actually want it to happen. You can also choose to provide the following optional parameters: terminal name, requester specified ID and action name (*default: "Log in"*). + +```csharp // Step 2 - Authenticate a log in -// With a pairing id and terminal name -AuthenticationRequest auth = api.Authenticate(pairing.id, "my computer"); -// With a username and terminal name and/or requester specified terminal id -AuthenticationRequest auth = api.Authenticate("username", terminalName: "terminalName", requesterSpecifiedId: "requesterSpecifiedId"); +AuthenticationRequest authenticationRequest = api.Authenticate("username@yourservice.com", "terminal name"); // Once they've responded you can then check the status -auth.RefreshFromServer(); -if (auth.pending == false && auth.granted == true) { +authenticationRequest.RefreshFromServer(); +if (authenticationRequest.pending == false && authenticationRequest.granted == true) { // Success! } ``` -#### Handling Errors +## ToopherIframe Workflow + +### Step 1: Embed a request in an IFRAME +1. Generate an authentication URL by providing a username. +2. Display a webpage to your user that embeds this URL within an `