Exemplo n.º 1
0
        /// <summary>
        /// DO NOT USE this from your code. Use <see cref="IGlobalXService.SafeAddOrUpdateGlobalXApiToken(GlobalXApiToken)"/>
        /// instead, because it will safely request a lock before calling this method.
        ///
        /// Adds or updates the supplied <see cref="GlobalXApiToken"/>. A lock must be requested before you can add or update
        /// a token, so you must supply a valid <see cref="GlobalXApiTokenLockInfo"/>.
        /// </summary>
        /// <param name="apiToken"></param>
        /// <param name="lockInfo"></param>
        /// <returns></returns>
        /// <exception cref="CannotUpdateGlobalXApiTokenWithoutLockException">If there is no existing lock for this token.</exception>
        /// <exception cref="GlobalXApiTokenLockIdMismatchException">
        ///     If the <see cref="GlobalXApiTokenLockInfo"/> supplied doesn't match the one stored for the token.
        /// </exception>
        public async Task AddOrUpdateGlobalXApiToken(GlobalXApiToken apiToken, GlobalXApiTokenLockInfo lockInfo, bool overrideAndClearLock = false)
        {
            if (apiToken is null)
            {
                throw new ArgumentNullException(nameof(apiToken));
            }
            if (lockInfo is null && !overrideAndClearLock)
            {
                throw new ArgumentNullException(nameof(lockInfo),
                                                $"Must not be null if {nameof(overrideAndClearLock)} is false.");
            }

            if (!(lockInfo is null))
            {
                var existingLock = await GetLock(lockInfo?.UserId);

                if (overrideAndClearLock)
                {
                    await DeleteLock(existingLock);
                }
                else
                {
                    if (existingLock is null)
                    {
                        throw new CannotUpdateGlobalXApiTokenWithoutLockException(apiToken);
                    }

                    if (existingLock.LockId != lockInfo.LockId)
                    {
                        throw new GlobalXApiTokenLockIdMismatchException(existingLock, lockInfo, apiToken);
                    }
                }
            }

            _telemetry.TrackEvent(nameof(AddOrUpdateGlobalXApiToken), new Dictionary <string, string>()
            {
                { "Access Token User Id", apiToken.UserId },
                { "Access Token Expires At", apiToken.AccessTokenExpiryUtc.ToString() },
            });

            // Store or update the expiry dates in table storage as a read model for quicker enumeration tokens nearing expiry
            DateTimeOffset?revokedAt = null;

            if (apiToken.RevokedAt.HasValue)
            {
                revokedAt = apiToken.RevokedAt.Value.ToDateTimeOffset();
            }

            await _expiryTableReference.ExecuteAsync(TableOperation.InsertOrReplace(new TokenExpiryTableEntity(
                                                                                        apiToken.RefreshTokenExpiryUtc.ToDateTimeUtc(),
                                                                                        apiToken.AccessTokenExpiryUtc.ToDateTimeUtc(),
                                                                                        apiToken.UserId,
                                                                                        revokedAt)));

            // Store the actual token data
            await _keyVaultClient.SetSecretAsync(
                _appSettings.CredentialAzureKeyVaultUrl,
                GenerateTokenId(apiToken.UserId),
                JsonConvert.SerializeObject(apiToken));
        }
Exemplo n.º 2
0
        public LockInfoTableEntity(GlobalXApiTokenLockInfo globalXApiTokenLockInfo)
        {
            if (globalXApiTokenLockInfo is null)
            {
                throw new ArgumentNullException(nameof(globalXApiTokenLockInfo));
            }

            PartitionKey = globalXApiTokenLockInfo.UserId;
            base.RowKey  = RowKey;

            LockId       = globalXApiTokenLockInfo.LockId;
            ExpiresAtUtc = globalXApiTokenLockInfo.ExpiresAt.ToDateTimeUtc();
        }
Exemplo n.º 3
0
        public async Task DeleteLock(GlobalXApiTokenLockInfo lockInfo)
        {
            if (lockInfo is null)
            {
                return;
            }

            var existingLockEntity = await GetLockInfoTableEntity(lockInfo.UserId);

            // If no existing lock, nothing to delete
            if (existingLockEntity is null)
            {
                return;
            }

            if (lockInfo.LockId == existingLockEntity.LockId)
            {
                await _lockTableReference.ExecuteAsync(TableOperation.Delete(existingLockEntity));
            }
        }
Exemplo n.º 4
0
        private async Task <GlobalXApiTokenLockInfo> RequestApiTokenLock(string userId)
        {
            if (string.IsNullOrEmpty(userId))
            {
                throw new ArgumentException("Must be supplied", nameof(userId));
            }

            var timeoutExpiry = _clock.GetCurrentInstant().Plus(_waitForExistingLockTimeout);
            GlobalXApiTokenLockInfo currentLock;

            do
            {
                currentLock = await _globalXApiTokenRepository.GetLock(userId);

                // No existing lock, or previous lock has expired. So we can attempt to create a new lock.
                if (currentLock is null || currentLock?.ExpiresAt <= _clock.GetCurrentInstant())
                {
                    var lockInfo = new GlobalXApiTokenLockInfo(userId, _clock.GetCurrentInstant().Plus(_requestLockDuration), _globalXApiTokenRepository);
                    await _globalXApiTokenRepository.SetLock(lockInfo);

                    // Read the lock back to ensure our lock won and was set correctly.
                    var lockInfoCheck = await _globalXApiTokenRepository.GetLock(userId);

                    // Only return the lock if it was successfully saved.
                    // If these don't match, then another lock got in before we did so we should
                    // let the loop continue and try again after the next polling interval.
                    if (lockInfo?.LockId == lockInfoCheck?.LockId)
                    {
                        return(lockInfoCheck);
                    }
                }

                await Task.Delay(_requestLockPollingInterval);
            } while (_clock.GetCurrentInstant() < timeoutExpiry);

            throw new GlobalXApiTokenRequestLockTimeoutException(currentLock, userId);
        }
Exemplo n.º 5
0
 public async Task SetLock(GlobalXApiTokenLockInfo lockInfo)
 {
     await _lockTableReference.ExecuteAsync(TableOperation.InsertOrReplace(new LockInfoTableEntity(lockInfo)));
 }