Lean  $LEAN_TAG$
Authentication.cs
1 /*
2  * QUANTCONNECT.COM - Democratizing Finance, Empowering Individuals.
3  * Lean Algorithmic Trading Engine v2.0. Copyright 2014 QuantConnect Corporation.
4  *
5  * Licensed under the Apache License, Version 2.0 (the "License");
6  * you may not use this file except in compliance with the License.
7  * You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0
8  *
9  * Unless required by applicable law or agreed to in writing, software
10  * distributed under the License is distributed on an "AS IS" BASIS,
11  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12  * See the License for the specific language governing permissions and
13  * limitations under the License.
14  *
15 */
16 
17 using System;
18 using System.Web;
19 using System.Text;
20 using Newtonsoft.Json;
21 using Newtonsoft.Json.Linq;
22 using System.Collections.Generic;
23 using System.Collections.Specialized;
24 
25 namespace QuantConnect.Api
26 {
27  /// <summary>
28  /// Helper methods for api authentication and interaction
29  /// </summary>
30  public static class Authentication
31  {
32  /// <summary>
33  /// Generate a secure hash for the authorization headers.
34  /// </summary>
35  /// <returns>Time based hash of user token and timestamp.</returns>
36  public static string Hash(int timestamp)
37  {
38  return Hash(timestamp, Globals.UserToken);
39  }
40 
41  /// <summary>
42  /// Generate a secure hash for the authorization headers.
43  /// </summary>
44  /// <returns>Time based hash of user token and timestamp.</returns>
45  public static string Hash(int timestamp, string token)
46  {
47  // Create a new hash using current UTC timestamp.
48  // Hash must be generated fresh each time.
49  var data = $"{token}:{timestamp.ToStringInvariant()}";
50  return data.ToSHA256();
51  }
52 
53  /// <summary>
54  /// Create an authenticated link for the target endpoint using the optional given payload
55  /// </summary>
56  /// <param name="endpoint">The endpoint</param>
57  /// <param name="payload">The payload</param>
58  /// <returns>The authenticated link to trigger the request</returns>
59  public static string Link(string endpoint, IEnumerable<KeyValuePair<string, object>> payload = null)
60  {
61  var queryString = HttpUtility.ParseQueryString(string.Empty);
62 
63  var timestamp = (int)Time.TimeStamp();
64  queryString.Add("authorization", Convert.ToBase64String(Encoding.UTF8.GetBytes($"{Globals.UserId}:{Hash(timestamp)}")));
65  queryString.Add("timestamp", timestamp.ToStringInvariant());
66 
67  PopulateQueryString(queryString, payload);
68 
69  return $"{Globals.Api}{endpoint.RemoveFromStart("/").RemoveFromEnd("/")}?{queryString}";
70  }
71 
72  /// <summary>
73  /// Helper method to populate a query string with the given payload
74  /// </summary>
75  /// <remarks>Useful for testing purposes</remarks>
76  public static void PopulateQueryString(NameValueCollection queryString, IEnumerable<KeyValuePair<string, object>> payload = null)
77  {
78  if (payload != null)
79  {
80  foreach (var kv in payload)
81  {
82  AddToQuery(queryString, kv);
83  }
84  }
85  }
86 
87  /// <summary>
88  /// Will add the given key value pairs to the query encoded as xform data
89  /// </summary>
90  private static void AddToQuery(NameValueCollection queryString, KeyValuePair<string, object> keyValuePairs)
91  {
92  var objectType = keyValuePairs.Value.GetType();
93  if (objectType.IsValueType || objectType == typeof(string))
94  {
95  // straight
96  queryString.Add(keyValuePairs.Key, keyValuePairs.Value.ToString());
97  }
98  else
99  {
100  // let's take advantage of json to load the properties we should include
101  var serialized = JsonConvert.SerializeObject(keyValuePairs.Value);
102  foreach (var jObject in JObject.Parse(serialized))
103  {
104  var subKey = $"{keyValuePairs.Key}[{jObject.Key}]";
105  if (jObject.Value is JObject)
106  {
107  // inception
108  AddToQuery(queryString, new KeyValuePair<string, object>(subKey, jObject.Value.ToObject<object>()));
109  }
110  else if(jObject.Value is JArray jArray)
111  {
112  var counter = 0;
113  foreach (var value in jArray.ToObject<List<object>>())
114  {
115  queryString.Add($"{subKey}[{counter++}]", value.ToString());
116  }
117  }
118  else
119  {
120  queryString.Add(subKey, jObject.Value.ToString());
121  }
122  }
123  }
124  }
125  }
126 }