/// <summary>Rolls a generic GET request to Atlas</summary> /// <param name="uri"></param> /// <returns></returns> protected async Task <HttpResponseMessage> GetRequest(Uri uri) { HttpResponseMessage final = null; if (uri == null || String.IsNullOrWhiteSpace(uri.AbsoluteUri)) { return(final); } // setup a new client HttpClient client = this.Client(uri); // setup the first request HttpRequestMessage req1 = new HttpRequestMessage(HttpMethod.Get, uri); req1.Headers.Clear(); // send the 'login' request HttpResponseMessage resp1 = await client.SendAsync(req1); // at this point we expect a 401 with details for the Authorization header // pull apart the auth response string authHeader = resp1.Headers.WwwAuthenticate?.ToString(); WWWAuthFields header = this.ParseWWWAuth(authHeader); // send a 2nd request with Authorization populated with details from www-authenticate HttpRequestMessage req2 = new HttpRequestMessage(HttpMethod.Get, uri); req2.Headers.Clear(); req2.Headers.Add("Authorization", GetDigestHeader(uri.AbsolutePath, header, HttpMethod.Get)); final = await client.SendAsync(req2); return(final); }
/// <summary>Pause or resume the specified cluster</summary> /// <param name="projID"></param> /// <param name="clusterName"></param> /// <param name="pause">Pass true to pause the cluster, false to resume the cluster</param> /// <returns></returns> /// <remarks>PATCH is similar to POST but Pause is the only method using this for now so its not yet broken out</remarks> public async Task <AtlasCluster> PauseCluster(string projID, string clusterName, bool pause = false) { if (String.IsNullOrWhiteSpace(projID) || String.IsNullOrWhiteSpace(clusterName)) { return(null); } projID = projID.Trim(); clusterName = clusterName.Trim(); Uri uri = new Uri($"{_apiBase}{_dir}/groups/{projID}/clusters/{clusterName}"); HttpResponseMessage final = null; try { // make a client HttpClient client = this.Client(uri); // roll the body string body = JsonConvert.SerializeObject(new { paused = pause }).ToLower(); HttpRequestMessage req1 = new HttpRequestMessage(HttpMethod.Patch, uri); req1.Headers.Clear(); // add the body req1.Content = new StringContent(body, Encoding.UTF8, "application/json"); byte[] bytes = Encoding.UTF8.GetBytes(body); req1.Content.Headers.Add("Content-Length", bytes.Length.ToString()); // send the first request HttpResponseMessage resp1 = await client.SendAsync(req1); // at this point we expect a 401 with details for the Authorization header // pull apart the auth response string authHeader = resp1.Headers.WwwAuthenticate?.ToString(); WWWAuthFields header = this.ParseWWWAuth(authHeader); // send a 2nd request with Authorization populated with details from www-authenticate HttpRequestMessage req2 = new HttpRequestMessage(HttpMethod.Patch, uri); req2.Headers.Clear(); req2.Headers.Add("Authorization", this.GetDigestHeader(uri.AbsolutePath, header, HttpMethod.Patch)); // add the body again req2.Content = req1.Content; // send the 2nd request final = await client.SendAsync(req2); } catch (System.Exception) { throw; } string json = (IsGZipped(final)) ? Decompress(final) : await final.Content.ReadAsStringAsync(); return(JsonConvert.DeserializeObject <AtlasCluster>(json)); }
/// <summary>Create an Auth Header Digest</summary> /// <remarks>If you continue to use the orginal Auth request you need to increment the nonce value each request</remarks> private string GetDigestHeader(string dir, WWWAuthFields header, HttpMethod method) { // increment on subsequent calls if you re-use int nc = 1; // create a client nonce string cnonce = this.Noncer(8); string h1 = $"{_publicKey}:{header.Realm}:{_privateKey}".HashToMD5(); string h2 = $"{method.ToString().ToUpperInvariant()}:{dir}".HashToMD5(); string resp = $"{h1}:{header.Nonce}:{nc:00000000}:{cnonce}:{header.QoP}:{h2}".HashToMD5(); return($"Digest username=\"{_publicKey}\", realm=\"{header.Realm}\", nonce=\"{header.Nonce}\", uri=\"{dir}\", algorithm=MD5, response=\"{resp}\", qop={header.QoP}, nc={nc:00000000}, cnonce=\"{cnonce}\""); }
/// <summary>Parses all the WWW Auth Fields to a struct</summary> /// <param name="header"></param> /// <returns></returns> /// <remarks> /// Use whatever parsing technique makes you happy. A couple are in StringExtensions /// An www auth header will look something like this /// Digest realm="MMS Public API", domain="", nonce="kWVA9Ciu7lNaN5QdjPe8kxPMReVjbt+B", algorithm=MD5, qop="auth", stale=false /// </remarks> private WWWAuthFields ParseWWWAuth(string header) { WWWAuthFields results = new WWWAuthFields(); if (String.IsNullOrWhiteSpace(header)) { return(results); } Dictionary <string, string> dict = header.PairsToDictionary(true); results.Realm = (dict.ContainsKey("realm")) ? dict["realm"] : String.Empty; results.Nonce = (dict.ContainsKey("nonce")) ? dict["nonce"] : String.Empty; results.QoP = (dict.ContainsKey("qop")) ? dict["qop"] : String.Empty; return(results); }