Skip to content

Commit

Permalink
Merge pull request #666 from squid808/CustomAuthParams
Browse files Browse the repository at this point in the history
Custom Auth Query Params
  • Loading branch information
peleyal committed Jan 30, 2016
2 parents d976456 + 1c3b5f8 commit ab2161a
Show file tree
Hide file tree
Showing 8 changed files with 233 additions and 7 deletions.
Expand Up @@ -100,6 +100,7 @@
<Compile Include="OAuth2\ComputeCredentialTests.cs" />
<Compile Include="OAuth2\Flows\AuthorizationCodeFlowTests.cs" />
<Compile Include="OAuth2\BearerTokenTests.cs" />
<Compile Include="OAuth2\Flows\GoogleAuthorizationCodeFlowTests.cs" />
<Compile Include="OAuth2\Requests\AuthorizationCodeRequestUrlTests.cs" />
<Compile Include="OAuth2\Requests\AuthorizationCodeTokenRequestTests.cs" />
<Compile Include="OAuth2\Requests\GoogleAuthorizationCodeRequestUrlTest.cs" />
Expand Down
@@ -0,0 +1,85 @@
/*
Copyright 2016 Google Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System.Collections.Generic;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;

using Moq;
using NUnit.Framework;

using Google.Apis.Auth.OAuth2.Flows;
using Google.Apis.Auth.OAuth2.Requests;
using Google.Apis.Auth.OAuth2.Responses;
using Google.Apis.Http;
using Google.Apis.Util.Store;

namespace Google.Apis.Auth.OAuth2.Flows
{
/// <summary>Tests for <see cref="Google.Apis.Auth.OAuth2.Flows.GoogleAuthorizationCodeFlow"/>.</summary>
public class GoogleAuthorizationCodeFlowTests
{
GoogleAuthorizationCodeFlow.Initializer initializer { get; set; }
List<KeyValuePair<string, string>> userDefinedParams { get; set; }

[SetUp]
public void GoogleAuthorizationCodeFlowTestSetup()
{
string revokeTokenUrl = "Revoke Token Url";
bool? includeGrantedScopes = true;
userDefinedParams = new List<KeyValuePair<string, string>>
{
new KeyValuePair<string,string>("customParam1","customVal1"),
new KeyValuePair<string,string>("customParam2","customVal2")
};

initializer = new GoogleAuthorizationCodeFlow.Initializer()
{
RevokeTokenUrl = revokeTokenUrl,
IncludeGrantedScopes = includeGrantedScopes,
UserDefinedQueryParams = userDefinedParams,
ClientSecrets = new ClientSecrets()
};
}

[Test]
public void ConstructorTest()
{
var flow = new GoogleAuthorizationCodeFlow(initializer);

Assert.That(flow.RevokeTokenUrl, Is.EqualTo(initializer.RevokeTokenUrl));
Assert.That(flow.IncludeGrantedScopes, Is.EqualTo(initializer.IncludeGrantedScopes));
Assert.That(flow.UserDefinedQueryParams, Is.EqualTo(initializer.UserDefinedQueryParams));
}

[Test]
public void CreateAuthorizationCodeRequestTest()
{
var flow = new GoogleAuthorizationCodeFlow(initializer);

var request = flow.CreateAuthorizationCodeRequest("TestRedirectUri") as GoogleAuthorizationCodeRequestUrl;

Assert.AreEqual(request.AccessType, "offline");
Assert.AreEqual(request.IncludeGrantedScopes, "true");
Assert.AreEqual(request.UserDefinedQueryParams, userDefinedParams);
Assert.AreEqual(request.RedirectUri, "TestRedirectUri");
}

//TODO(squid808): Add tests for RevokeToken.
}
}
Expand Up @@ -15,6 +15,7 @@
*/

using System;
using System.Collections.Generic;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
Expand All @@ -40,12 +41,21 @@ public class GoogleAuthorizationCodeFlow : AuthorizationCodeFlow
/// <summary>Gets or sets the include granted scopes indicator.</summary>
public bool? IncludeGrantedScopes { get { return includeGrantedScopes; } }

private readonly IEnumerable<KeyValuePair<string, string>> userDefinedQueryParams;

/// <summary>Gets the user defined query parameters.</summary>
public IEnumerable<KeyValuePair<string, string>> UserDefinedQueryParams
{
get { return userDefinedQueryParams; }
}

/// <summary>Constructs a new Google authorization code flow.</summary>
public GoogleAuthorizationCodeFlow(Initializer initializer)
: base(initializer)
{
revokeTokenUrl = initializer.RevokeTokenUrl;
includeGrantedScopes = initializer.IncludeGrantedScopes;
userDefinedQueryParams = initializer.UserDefinedQueryParams;
}

public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(string redirectUri)
Expand All @@ -56,7 +66,8 @@ public override AuthorizationCodeRequestUrl CreateAuthorizationCodeRequest(strin
Scope = string.Join(" ", Scopes),
RedirectUri = redirectUri,
IncludeGrantedScopes = IncludeGrantedScopes.HasValue
? IncludeGrantedScopes.Value.ToString().ToLower() : null
? IncludeGrantedScopes.Value.ToString().ToLower() : null,
UserDefinedQueryParams = UserDefinedQueryParams
};
}

Expand Down Expand Up @@ -91,9 +102,14 @@ public new class Initializer : AuthorizationCodeFlow.Initializer
/// <summary>Gets or sets the token revocation URL.</summary>
public string RevokeTokenUrl { get; set; }

/// <summary>Gets or sets the optional indicator for including granted scopes for incremental authorization.</summary>
/// <summary>
/// Gets or sets the optional indicator for including granted scopes for incremental authorization.
/// </summary>
public bool? IncludeGrantedScopes { get; set; }

/// <summary>Gets or sets the optional user defined query parameters.</summary>
public IEnumerable<KeyValuePair<string, string>> UserDefinedQueryParams { get; set; }

/// <summary>
/// Constructs a new initializer. Sets Authorization server URL to
/// <see cref="Google.Apis.Auth.OAuth2.GoogleAuthConsts.AuthorizationUrl"/>, and Token server URL to
Expand Down
Expand Up @@ -15,6 +15,7 @@
*/

using System;
using System.Collections.Generic;

namespace Google.Apis.Auth.OAuth2.Requests
{
Expand Down Expand Up @@ -59,6 +60,18 @@ public class GoogleAuthorizationCodeRequestUrl : AuthorizationCodeRequestUrl
Google.Apis.Util.RequestParameterType.Query)]
public string IncludeGrantedScopes { get; set; }

/// <summary>
/// Gets or sets a collection of user defined query parameters to facilitate any not explicitly supported
/// by the library which will be included in the resultant authentication URL.
/// </summary>
/// <remarks>
/// The name of this parameter is used only for the constructor and will not end up in the resultant query
/// string.
/// </remarks>
[Google.Apis.Util.RequestParameterAttribute("user_defined_query_params",
Google.Apis.Util.RequestParameterType.UserDefinedQueries)]
public IEnumerable<KeyValuePair<string, string>> UserDefinedQueryParams { get; set; }

/// <summary>
/// Constructs a new authorization code request with the given authorization server URL. This constructor sets
/// the <see cref="AccessType"/> to <c>offline</c>.
Expand Down
Expand Up @@ -20,6 +20,7 @@
using System.Linq;
using System.Reflection;

using Google.Apis.Logging;
using Google.Apis.Util;

namespace Google.Apis.Requests.Parameters
Expand All @@ -29,6 +30,8 @@ namespace Google.Apis.Requests.Parameters
/// </summary>
public static class ParameterUtils
{
private static readonly ILogger Logger = ApplicationContext.Logger.ForType(typeof(ParameterUtils));

/// <summary>
/// Creates a <see cref="System.Net.Http.FormUrlEncodedContent"/> with all the specified parameters in
/// the input request. It uses reflection to iterate over all properties with
Expand Down Expand Up @@ -111,7 +114,7 @@ private static void IterateParameters(object request, Action<RequestParameterTyp
continue;
}

// Get the name of this parameter from the attribute, if it doesn't exist take a lower-case variant of
// Get the name of this parameter from the attribute, if it doesn't exist take a lower-case variant of
// property name.
string name = attribute.Name ?? property.Name.ToLower();

Expand All @@ -121,7 +124,25 @@ private static void IterateParameters(object request, Action<RequestParameterTyp
// Call action with the type name and value.
if (propertyType.IsValueType || value != null)
{
action(attribute.Type, name, value);
if (attribute.Type == RequestParameterType.UserDefinedQueries)
{
if (typeof(IEnumerable<KeyValuePair<string, string>>).IsAssignableFrom(value.GetType()))
{
foreach (var pair in (IEnumerable<KeyValuePair<string, string>>)value)
{
action(RequestParameterType.Query, pair.Key, pair.Value);
}
}
else
{
Logger.Warning("Parameter marked with RequestParameterType.UserDefinedQueries attribute " +
"was not of type IEnumerable<KeyValuePair<string, string>> and will be skipped.");
}
}
else
{
action(attribute.Type, name, value);
}
}
}
}
Expand Down
Expand Up @@ -56,21 +56,27 @@ public RequestParameterAttribute(string name)
/// string value into the path, replacing {name}. If the parameter is a query parameter, this parameter will be
/// added to the query string, in the format "name=value".
/// </param>
/// <param name="type">The type of the parameter, either Path or Query.</param>
/// <param name="type">The type of the parameter, either Path, Query or UserDefinedQueries.</param>
public RequestParameterAttribute(string name, RequestParameterType type)
{
this.name = name;
this.type = type;
}
}

/// <summary>Describe the type of this parameter (Path or Query).</summary>
/// <summary>Describe the type of this parameter (Path, Query or UserDefinedQueries).</summary>
public enum RequestParameterType
{
/// <summary>A path parameter which is inserted into the path portion of the request URI.</summary>
Path,

/// <summary>A query parameter which is inserted into the query portion of the request URI.</summary>
Query
Query,

/// <summary>
/// A group of user-defined parameters that will be added in to the query portion of the request URI. If this
/// type is being used, the name of the RequestParameterAttirbute is meaningless.
/// </summary>
UserDefinedQueries
}
}
@@ -0,0 +1,83 @@
/*
Copyright 2016 Google Inc
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/

using System.Collections.Generic;

using NUnit.Framework;

using Google.Apis.Util;
using Google.Apis.Requests;
using Google.Apis.Requests.Parameters;

namespace Google.Apis.Tests.Apis.Requests
{
/// <summary>Tests for <see cref="Google.Apis.Requests.Parameters.ParameterUtils"/>.</summary>
[TestFixture]
public class ParameterUtilsTest
{
private class TestRequestUrl
{
[Google.Apis.Util.RequestParameterAttribute("first_query_param",
Google.Apis.Util.RequestParameterType.Query)]
public string FirstParam { get; set; }

[Google.Apis.Util.RequestParameterAttribute("second_query_param",
Google.Apis.Util.RequestParameterType.Query)]
public string SecondParam { get; set; }

[Google.Apis.Util.RequestParameterAttribute("query_param_attribute_name",
Google.Apis.Util.RequestParameterType.UserDefinedQueries)]
public List<KeyValuePair<string, string>> ParamsCollection { get; set; }

public System.Uri Build()
{
var builder = new RequestBuilder()
{
BaseUri = new System.Uri("//revokeTokenUrl")
};
ParameterUtils.InitParameters(builder, this);
return builder.BuildUri();
}
}

[Test]
public void IterateParametersTest()
{
var request = new TestRequestUrl()
{
FirstParam = "firstOne",
SecondParam = "secondOne",
ParamsCollection = new List<KeyValuePair<string, string>>{
new KeyValuePair<string,string>("customParam1","customVal1"),
new KeyValuePair<string,string>("customParam2","customVal2")
}
};

var result = request.Build().AbsoluteUri;

//Parametera were given a name and a value, so both appear in the URI.
Assert.That(result, Contains.Substring("first_query_param=firstOne"));
Assert.That(result, Contains.Substring("second_query_param=secondOne"));

//Custom parameters are key value pairs representing parameter names and values respectively.
Assert.That(result, Contains.Substring("customParam1=customVal1"));
Assert.That(result, Contains.Substring("customParam2=customVal2"));

//The parameter name for the custom parameters does not carry through to the resulting URI.
Assert.IsFalse(result.Contains("query_param_attribute_name"));
}
}
}
1 change: 1 addition & 0 deletions Src/Support/GoogleApis.Tests/GoogleApis.Tests.csproj
Expand Up @@ -138,6 +138,7 @@
<Compile Include="Apis\Requests\BatchRequestTest.cs" />
<Compile Include="Apis\Requests\PageStreamerTest.cs" />
<Compile Include="Apis\Requests\Parameters\ParameterCollectionTest.cs" />
<Compile Include="Apis\Requests\Parameters\ParameterUtilsTest.cs" />
<Compile Include="Apis\Requests\Parameters\ParameterValidatorTest.cs" />
<Compile Include="Apis\Requests\RequestBuilderTest.cs" />
<Compile Include="Apis\Services\BaseClientServiceTest.cs" />
Expand Down

0 comments on commit ab2161a

Please sign in to comment.