This repository has been archived by the owner on Nov 9, 2017. It is now read-only.
/
TokenProvider.cs
209 lines (176 loc) · 8.29 KB
/
TokenProvider.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
namespace ArcGIS.ServiceModel
{
using Logging;
using Operation;
using Operation.Admin;
using System;
using System.Linq;
using System.Net.Http;
using System.Threading;
using System.Threading.Tasks;
/// <summary>
/// ArcGIS Server token provider
/// </summary>
public class TokenProvider : ITokenProvider, IDisposable
{
HttpClient _httpClient;
protected GenerateToken TokenRequest;
Token _token;
PublicKeyResponse _publicKey;
protected bool CanAccessPublicKeyEndpoint = true;
readonly ILog _logger;
/// <summary>
/// Create a token provider to authenticate against ArcGIS Server
/// </summary>
/// <param name="rootUrl">Made up of scheme://host:port/site</param>
/// <param name="username">ArcGIS Server user name</param>
/// <param name="password">ArcGIS Server user password</param>
/// <param name="serializer">Used to (de)serialize requests and responses</param>
/// <param name="referer">Referer url to use for the token generation</param>
/// <param name="cryptoProvider">Used to encrypt the token reuqest. If not set it will use the default from CryptoProviderFactory</param>
public TokenProvider(string rootUrl, string username, string password, ISerializer serializer = null, string referer = "", ICryptoProvider cryptoProvider = null)
: this (() => LogProvider.For<TokenProvider>(), rootUrl, username, password, serializer, referer, cryptoProvider)
{ }
internal TokenProvider(Func<ILog> log, string rootUrl, string username, string password, ISerializer serializer = null, string referer = "", ICryptoProvider cryptoProvider = null)
{
if (string.IsNullOrWhiteSpace(rootUrl)) throw new ArgumentNullException(nameof(rootUrl), "rootUrl is null.");
if (string.IsNullOrWhiteSpace(username)) throw new ArgumentNullException(nameof(username), "username is null.");
if (string.IsNullOrWhiteSpace(password)) throw new ArgumentNullException(nameof(password), "password is null.");
Serializer = serializer ?? SerializerFactory.Get();
Guard.AgainstNullArgument("Serializer", Serializer);
RootUrl = rootUrl.AsRootUrl();
CryptoProvider = cryptoProvider ?? CryptoProviderFactory.Get();
_httpClient = HttpClientFactory.Get();
TokenRequest = new GenerateToken(username, password) { Referer = referer };
UserName = username;
_logger = log() ?? LogProvider.For<TokenProvider>();
_logger.DebugFormat("Created new token provider for {0}", RootUrl);
}
~TokenProvider()
{
Dispose(false);
}
protected virtual void Dispose(bool disposing)
{
if (disposing)
{
if (_httpClient != null)
{
_httpClient.Dispose();
_httpClient = null;
}
_token = null;
}
}
public void Dispose()
{
Dispose(true);
GC.SuppressFinalize(this);
}
public bool CancelPendingRequests { get; set; }
public ICryptoProvider CryptoProvider { get; private set; }
public string RootUrl { get; private set; }
public string UserName { get; private set; }
public ISerializer Serializer { get; private set; }
void CheckRefererHeader()
{
if (_httpClient == null || string.IsNullOrWhiteSpace(TokenRequest.Referer)) return;
Uri referer;
bool validReferrerUrl = Uri.TryCreate(TokenRequest.Referer, UriKind.Absolute, out referer);
if (!validReferrerUrl)
{
throw new HttpRequestException(string.Format("Not a valid url for referrer: {0}", TokenRequest.Referer));
}
_httpClient.DefaultRequestHeaders.Referrer = referer;
}
//Token _token;
/// <summary>
/// Generates a token using the username and password set for this provider.
/// </summary>
/// <returns>The generated token or null if not applicable</returns>
/// <remarks>This sets the Token property for the provider. It will be auto appended to
/// any requests sent through the gateway used by this provider.</remarks>
public async Task<Token> CheckGenerateToken(CancellationToken ct = default(CancellationToken))
{
if (TokenRequest == null) return null;
if (_token != null && !_token.IsExpired) return _token;
_token = null; // reset the Token
_publicKey = null;
CheckRefererHeader();
var url = TokenRequest.BuildAbsoluteUrl(RootUrl).Split('?').FirstOrDefault();
Uri uri;
bool validUrl = Uri.TryCreate(url, UriKind.Absolute, out uri);
if (!validUrl)
{
throw new HttpRequestException(string.Format("Not a valid url: {0}", url));
}
_logger.DebugFormat("Token request {0}", uri.AbsoluteUri);
if (CryptoProvider != null && _publicKey == null && CanAccessPublicKeyEndpoint)
{
var publicKey = new PublicKey();
var encryptionInfoEndpoint = publicKey.BuildAbsoluteUrl(RootUrl) + PortalGatewayBase.AsRequestQueryString(Serializer, publicKey);
_logger.DebugFormat("Encrypted token request {0}", encryptionInfoEndpoint);
string publicKeyResultString = null;
try
{
if (CancelPendingRequests)
{
_httpClient.CancelPendingRequests();
}
HttpResponseMessage response = await _httpClient.GetAsync(encryptionInfoEndpoint, ct).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
publicKeyResultString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
catch (TaskCanceledException tce)
{
_logger.WarnException("Token request cancelled (exception swallowed)", tce);
return default(Token);
}
catch (HttpRequestException hex)
{
_logger.WarnException("Token request exception (exception swallowed)", hex);
CanAccessPublicKeyEndpoint = false;
}
if (ct.IsCancellationRequested) return null;
if (CanAccessPublicKeyEndpoint)
{
_publicKey = Serializer.AsPortalResponse<PublicKeyResponse>(publicKeyResultString);
if (_publicKey.Error != null)
{
throw new InvalidOperationException(_publicKey.Error.ToString());
}
TokenRequest = CryptoProvider.Encrypt(TokenRequest, _publicKey.Exponent, _publicKey.Modulus);
}
}
if (ct.IsCancellationRequested) return null;
HttpContent content = new FormUrlEncodedContent(Serializer.AsDictionary(TokenRequest));
if (CancelPendingRequests)
{
_httpClient.CancelPendingRequests();
}
string resultString = string.Empty;
try
{
HttpResponseMessage response = await _httpClient.PostAsync(uri, content, ct).ConfigureAwait(false);
response.EnsureSuccessStatusCode();
resultString = await response.Content.ReadAsStringAsync().ConfigureAwait(false);
}
catch (TaskCanceledException tce)
{
_logger.WarnException("Token request cancelled (exception swallowed)", tce);
return default(Token);
}
var result = Serializer.AsPortalResponse<Token>(resultString);
if (result.Error != null)
{
throw new InvalidOperationException(result.Error.ToString());
}
if (!string.IsNullOrWhiteSpace(TokenRequest.Referer))
{
result.Referer = TokenRequest.Referer;
}
_token = result;
return _token;
}
}
}