/// <summary> Refresh the Paratext access token if expired with the given HttpClient. </summary> public async Task <Tokens> RefreshAccessTokenAsync(ParatextOptions options, Tokens paratextTokens, HttpClient client) { bool expired = !paratextTokens.ValidateLifetime(); if (!expired) { return(paratextTokens); } using (var request = new HttpRequestMessage(HttpMethod.Post, "api8/token")) { var requestObj = new JObject( new JProperty("grant_type", "refresh_token"), new JProperty("client_id", options.ClientId), new JProperty("client_secret", options.ClientSecret), new JProperty("refresh_token", paratextTokens.RefreshToken)); request.Content = new StringContent(requestObj.ToString(), Encoding.Default, "application/json"); HttpResponseMessage response = await client.SendAsync(request); await _exceptionHandler.EnsureSuccessStatusCode(response); string responseJson = await response.Content.ReadAsStringAsync(); JObject responseObj = JObject.Parse(responseJson); return(new Tokens { AccessToken = (string)responseObj["access_token"], RefreshToken = (string)responseObj["refresh_token"] }); } }
private async Task RefreshAccessTokenAsync(User user) { var request = new HttpRequestMessage(HttpMethod.Post, "api8/token"); ParatextOptions options = _options.Value; var requestObj = new JObject( new JProperty("grant_type", "refresh_token"), new JProperty("client_id", options.ClientId), new JProperty("client_secret", options.ClientSecret), new JProperty("refresh_token", user.ParatextAccessToken.RefreshToken)); request.Content = new StringContent(requestObj.ToString(), Encoding.UTF8, "application/json"); HttpResponseMessage response = await _registryClient.SendAsync(request); response.EnsureSuccessStatusCode(); string responseJson = await response.Content.ReadAsStringAsync(); var responseObj = JObject.Parse(responseJson); user.ParatextAccessToken = new AccessTokenInfo { IdToken = (string)responseObj["id_token"], AccessToken = (string)responseObj["access_token"], RefreshToken = (string)responseObj["refresh_token"] }; await _userRepo.UpdateAsync(user, b => b.Set(u => u.ParatextAccessToken, user.ParatextAccessToken)); }
/// <summary> /// Initializes a new instance of the <see cref="SFInstallableDblResource" /> class. /// </summary> /// <param name="userSecret">The user secret.</param> /// <param name="paratextOptions">The paratext options.</param> /// <param name="restClientFactory">The rest client factory.</param> /// <param name="fileSystemService">The file system service.</param> /// <param name="jwtTokenHelper">The JWT token helper.</param> /// <remarks> /// This is a convenience constructor for unit tests. /// </remarks> internal SFInstallableDblResource(UserSecret userSecret, ParatextOptions paratextOptions, ISFRestClientFactory restClientFactory, IFileSystemService fileSystemService, IJwtTokenHelper jwtTokenHelper) : this(userSecret, paratextOptions, restClientFactory, fileSystemService, jwtTokenHelper, new ParatextProjectDeleter(), new ParatextMigrationOperations(), new ParatextZippedResourcePasswordProvider(paratextOptions)) { }
private async Task RefreshAccessTokenAsync(UserSecret userSecret) { ParatextOptions options = _paratextOptions.Value; userSecret.ParatextTokens = await _jwtTokenHelper.RefreshAccessTokenAsync(options, userSecret.ParatextTokens, _registryClient); await _userSecretRepository.UpdateAsync(userSecret, b => b.Set(u => u.ParatextTokens, userSecret.ParatextTokens)); }
/// <summary> /// Initializes a new instance of the <see cref="SFInstallableDblResource" /> class. /// </summary> /// <param name="userSecret">The user secret.</param> /// <param name="paratextOptions">The paratext options.</param> /// <param name="restClientFactory">The rest client factory.</param> /// <param name="fileSystemService">The file system service.</param> /// <param name="jwtTokenHelper">The JWT token helper.</param> /// <param name="projectDeleter">The project deleter.</param> /// <param name="migrationOperations">The migration operations.</param> /// <param name="passwordProvider">The password provider.</param> /// <exception cref="ArgumentNullException">restClientFactory</exception> private SFInstallableDblResource(UserSecret userSecret, ParatextOptions paratextOptions, ISFRestClientFactory restClientFactory, IFileSystemService fileSystemService, IJwtTokenHelper jwtTokenHelper, IProjectDeleter projectDeleter, IMigrationOperations migrationOperations, IZippedResourcePasswordProvider passwordProvider) : base(projectDeleter, migrationOperations, passwordProvider) { this._userSecret = userSecret; this._paratextOptions = paratextOptions; this._restClientFactory = restClientFactory; this._fileSystemService = fileSystemService; this._jwtTokenHelper = jwtTokenHelper; if (this._restClientFactory == null) { throw new ArgumentNullException(nameof(restClientFactory)); } else if (this._fileSystemService == null) { throw new ArgumentNullException(nameof(fileSystemService)); } }
/// <summary> /// Initializes a new instance of the <see cref="ParatextZippedResourcePasswordProvider"/> class. /// </summary> /// <param name="paratextOptions">The paratext options.</param> internal ParatextZippedResourcePasswordProvider(ParatextOptions paratextOptions) { this._paratextOptions = paratextOptions; }
/// <summary> /// Converts the JSON response to a list of Installable DBL Resources. /// </summary> /// <param name="baseUri">The base URI.</param> /// <param name="response">The response.</param> /// <param name="restClientFactory">The rest client factory.</param> /// <param name="fileSystemService">The file system service.</param> /// <param name="jwtTokenHelper">The JWT token helper.</param> /// <param name="createdTimestamp">The created timestamp.</param> /// <param name="userSecret">The user secret.</param> /// <param name="paratextOptions">The paratext options.</param> /// <param name="projectDeleter">The project deleter.</param> /// <param name="migrationOperations">The migration operations.</param> /// <param name="passwordProvider">The password provider.</param> /// <returns> /// The Installable Resources. /// </returns> private static IEnumerable <SFInstallableDblResource> ConvertJsonResponseToInstallableDblResources( string baseUri, string response, ISFRestClientFactory restClientFactory, IFileSystemService fileSystemService, IJwtTokenHelper jwtTokenHelper, DateTime createdTimestamp, UserSecret userSecret, ParatextOptions paratextOptions, IProjectDeleter projectDeleter, IMigrationOperations migrationOperations, IZippedResourcePasswordProvider passwordProvider) { if (!string.IsNullOrWhiteSpace(response)) { JObject jsonResources; try { jsonResources = JObject.Parse(response); } catch (JsonReaderException) { // Ignore the exception and just return empty result // This is probably caused by partial result from poor connection to DBL yield break; } foreach (JToken jsonResource in jsonResources["resources"] as JArray ?? new JArray()) { var name = (string)jsonResource["name"]; var nameCommon = (string)jsonResource["nameCommon"]; var fullname = (string)jsonResource["fullname"]; if (string.IsNullOrWhiteSpace(fullname)) { fullname = nameCommon; } var languageName = (string)jsonResource["languageName"]; var id = (string)jsonResource["id"]; var revision = (string)jsonResource["revision"]; var permissionsChecksum = (string)jsonResource["permissions-checksum"]; var manifestChecksum = (string)jsonResource["p8z-manifest-checksum"]; var languageIdLdml = (string)jsonResource["languageLDMLId"]; var languageIdCode = (string)jsonResource["languageCode"]; LanguageId languageId = migrationOperations.DetermineBestLangIdToUseForResource(languageIdLdml, languageIdCode); if (string.IsNullOrEmpty(languageId.Id)) { languageId = LanguageIdHelper.FromCommonLanguageName(languageName); } else { languageId = LanguageId.FromEthnologueCode(languageId.Id); } string url = BuildDblResourceEntriesUrl(baseUri, id); var resource = new SFInstallableDblResource(userSecret, paratextOptions, restClientFactory, fileSystemService, jwtTokenHelper, projectDeleter, migrationOperations, passwordProvider) { DisplayName = name, Name = name, FullName = fullname, LanguageID = languageId, DblSourceUrl = url, DBLEntryUid = id, DBLRevision = int.Parse(revision), PermissionsChecksum = permissionsChecksum, ManifestChecksum = manifestChecksum, CreatedTimestamp = createdTimestamp, }; resource.LanguageName = MacroLanguageHelper.GetMacroLanguage(resource.LanguageID) ?? languageName; yield return(resource); } } }
/// <summary> /// Return a list of resources which this user is allowed to install from DBL. /// If we cannot contact DBL, return an empty list. /// </summary> /// <param name="userSecret">The user secret.</param> /// <param name="paratextOptions">The paratext options.</param> /// <param name="restClientFactory">The rest client factory.</param> /// <param name="fileSystemService">The file system service.</param> /// <param name="jwtTokenHelper">The JWT token helper.</param> /// <param name="baseUrl">The base URL.</param> /// <returns>The Installable Resources.</returns> /// <exception cref="ArgumentNullException">restClientFactory /// or /// userSecret</exception> /// <remarks>Tests on this method can be found in ParatextServiceTests.cs calling GetResources().</remarks> public static IEnumerable <SFInstallableDblResource> GetInstallableDblResources(UserSecret userSecret, ParatextOptions paratextOptions, ISFRestClientFactory restClientFactory, IFileSystemService fileSystemService, IJwtTokenHelper jwtTokenHelper, IExceptionHandler exceptionHandler, string baseUrl = null) { // Parameter check (just like the constructor) if (restClientFactory == null) { throw new ArgumentNullException(nameof(restClientFactory)); } else if (userSecret == null) { throw new ArgumentNullException(nameof(userSecret)); } ISFRestClient client = restClientFactory.Create(string.Empty, ApplicationProduct.DefaultVersion, userSecret); baseUrl = string.IsNullOrWhiteSpace(baseUrl) ? InternetAccess.ParatextDBLServer : baseUrl; string response = null; try { response = client.Get(BuildDblResourceEntriesUrl(baseUrl)); } catch (WebException e) { // If we get a temporary 401 Unauthorized response, return an empty list. string errorExplanation = "GetInstallableDblResources failed when attempting to inquire about" + $" resources and is ignoring error {e}"; var report = new Exception(errorExplanation); // Report to bugsnag, but don't throw. exceptionHandler.ReportException(report); return(Enumerable.Empty <SFInstallableDblResource>()); } IEnumerable <SFInstallableDblResource> resources = ConvertJsonResponseToInstallableDblResources(baseUrl, response, restClientFactory, fileSystemService, jwtTokenHelper, DateTime.Now, userSecret, paratextOptions, new ParatextProjectDeleter(), new ParatextMigrationOperations(), new ParatextZippedResourcePasswordProvider(paratextOptions)); return(resources); }
/// <summary> /// Checks the resource permission. /// </summary> /// <param name="id">The identifier.</param> /// <param name="userSecret">The user secret.</param> /// <param name="paratextOptions">The paratext options.</param> /// <param name="restClientFactory">The rest client factory.</param> /// <param name="fileSystemService">The file system service.</param> /// <param name="jwtTokenHelper">The JWT token helper.</param> /// <param name="baseUrl">The base URL.</param> /// <returns> /// <c>true</c> if the user has permission to access the resource; otherwise, <c>false</c>. /// </returns> /// <exception cref="ArgumentNullException">id /// or /// userSecret /// or /// restClientFactory</exception> public static bool CheckResourcePermission(string id, UserSecret userSecret, ParatextOptions paratextOptions, ISFRestClientFactory restClientFactory, IFileSystemService fileSystemService, IJwtTokenHelper jwtTokenHelper, IExceptionHandler exceptionHandler, string baseUrl = null) { // Parameter check if (string.IsNullOrWhiteSpace(id)) { throw new ArgumentNullException(nameof(id)); } else if (userSecret == null) { throw new ArgumentNullException(nameof(userSecret)); } else if (restClientFactory == null) { throw new ArgumentNullException(nameof(restClientFactory)); } ISFRestClient client = restClientFactory.Create(string.Empty, ApplicationProduct.DefaultVersion, userSecret); baseUrl = string.IsNullOrWhiteSpace(baseUrl) ? InternetAccess.ParatextDBLServer : baseUrl; try { _ = client.Head(BuildDblResourceEntriesUrl(baseUrl, id)); return(true); } catch (Exception ex) { // Normally we would catch the specific WebException, // but something in ParatextData is interfering with it. if (ex.InnerException?.Message.StartsWith("401: ", StringComparison.OrdinalIgnoreCase) ?? false) { // A 401 error means unauthorized (probably a bad token) return(false); } else if (ex.InnerException?.Message.StartsWith("403: ", StringComparison.OrdinalIgnoreCase) ?? false) { // A 403 error means no access. return(false); } else if (ex.InnerException?.Message.StartsWith("404: ", StringComparison.OrdinalIgnoreCase) ?? false) { // A 404 error means that the resource is not on the server return(false); } else if (ex.InnerException?.Message.StartsWith("405: ", StringComparison.OrdinalIgnoreCase) ?? false) { // A 405 means that HEAD request does not work on the server, so we will use the resource list // This is slower (although faster than a GET request on the resource), but more reliable IEnumerable <SFInstallableDblResource> resources = GetInstallableDblResources( userSecret, paratextOptions, restClientFactory, fileSystemService, jwtTokenHelper, exceptionHandler, baseUrl); return(resources.Any(r => r.DBLEntryUid == id)); } else if (ex.Source == "NSubstitute") { // This occurs during unit tests to test whether there is permission or not return(false); } else { // An unknown error throw; } } }