Exemplo n.º 1
0
        /// <summary>
        /// Response for missing/Invalid headers.
        /// </summary>
        /// <param name="ignoreMissingAuth">Whether or not errors due to missing <see cref="HeaderNames.Authorization"/> should be thrown.</param>
        /// <returns>The appropriate <see cref="IActionResult"/>.</returns>
        protected IActionResult HeadersIssue(bool ignoreMissingAuth)
        {
            HeadersException headersException;

            try
            {
                var _ = new ApiHeaders(Request.GetTypedHeaders(), ignoreMissingAuth);
                throw new InvalidOperationException("Expected a header parse exception!");
            }
            catch (HeadersException ex)
            {
                headersException = ex;
            }

            var errorMessage = new ErrorMessageResponse(ErrorCode.BadHeaders)
            {
                AdditionalData = headersException.Message
            };

            if (headersException.MissingOrMalformedHeaders.HasFlag(HeaderTypes.Accept))
            {
                return(StatusCode(HttpStatusCode.NotAcceptable, errorMessage));
            }

            return(BadRequest(errorMessage));
        }
Exemplo n.º 2
0
        public void TestConstruction()
        {
            Assert.ThrowsException <ArgumentNullException>(() => new ApiHeaders(null, null));
            Assert.ThrowsException <ArgumentNullException>(() => new ApiHeaders(productHeaderValue, null));
            var headers = new ApiHeaders(productHeaderValue, String.Empty);

            headers = new ApiHeaders(productHeaderValue, String.Empty, OAuthProvider.GitHub);
        }
        /// <inheritdoc />
        public async Task <IServerClient> CreateFromLogin(
            Uri host,
            string username,
            string password,
            IEnumerable <IRequestLogger>?requestLoggers = null,
            TimeSpan?timeout                    = null,
            bool attemptRefreshLogin            = true,
            CancellationToken cancellationToken = default)
        {
            if (host == null)
            {
                throw new ArgumentNullException(nameof(host));
            }
            if (username == null)
            {
                throw new ArgumentNullException(nameof(username));
            }
            if (password == null)
            {
                throw new ArgumentNullException(nameof(password));
            }

            requestLoggers ??= Enumerable.Empty <IRequestLogger>();

            Token token;
            var   loginHeaders = new ApiHeaders(productHeaderValue, username, password);

            using (var api = ApiClientFactory.CreateApiClient(host, loginHeaders, null))
            {
                foreach (var requestLogger in requestLoggers)
                {
                    api.AddRequestLogger(requestLogger);
                }

                if (timeout.HasValue)
                {
                    api.Timeout = timeout.Value;
                }
                token = await api.Update <Token>(Routes.Root, cancellationToken).ConfigureAwait(false);
            }

            var apiHeaders = new ApiHeaders(productHeaderValue, token.Bearer !);

            var client = new ServerClient(ApiClientFactory.CreateApiClient(host, apiHeaders, loginHeaders), token);

            if (timeout.HasValue)
            {
                client.Timeout = timeout.Value;
            }

            foreach (var requestLogger in requestLoggers)
            {
                client.AddRequestLogger(requestLogger);
            }

            return(client);
        }
Exemplo n.º 4
0
#pragma warning disable CA1506
        public async Task <IActionResult> Home(CancellationToken cancellationToken)
        {
            if (controlPanelConfiguration.Enable)
            {
                Response.Headers.Add(
                    HeaderNames.Vary,
                    new StringValues(
                        new[] {
                    HeaderNames.UserAgent,
                    ApiHeaders.ApiVersionHeader
                }));
            }

            // we only allow authorization header issues
            if (ApiHeaders == null)
            {
                // if we are using a browser and the control panel, redirect to the app page
                if (controlPanelConfiguration.Enable && browserResolver.Browser.Type != BrowserType.Generic)
                {
                    Logger.LogDebug("Unauthorized browser request (User-Agent: \"{0}\"), redirecting to control panel...", browserResolver.UserAgent);
                    return(Redirect(Core.Application.ControlPanelRoute));
                }

                try
                {
                    var headers = new ApiHeaders(Request.GetTypedHeaders(), true);
                    if (!headers.Compatible())
                    {
                        return(StatusCode(
                                   HttpStatusCode.UpgradeRequired,
                                   new ErrorMessage(ErrorCode.ApiMismatch)));
                    }
                }
                catch (HeadersException)
                {
                    return(HeadersIssue(true));
                }
            }

            return(Json(new ServerInformation
            {
                Version = assemblyInformationProvider.Version,
                ApiVersion = ApiHeaders.Version,
                DMApiVersion = DMApiConstants.Version,
                MinimumPasswordLength = generalConfiguration.MinimumPasswordLength,
                InstanceLimit = generalConfiguration.InstanceLimit,
                UserLimit = generalConfiguration.UserLimit,
                UserGroupLimit = generalConfiguration.UserGroupLimit,
                ValidInstancePaths = generalConfiguration.ValidInstancePaths,
                WindowsHost = platformIdentifier.IsWindows,
                SwarmServers = swarmService.GetSwarmServers(),
                OAuthProviderInfos = await oAuthProviders.ProviderInfos(cancellationToken).ConfigureAwait(false),
                UpdateInProgress = serverControl.UpdateInProgress,
            }));
        }
 /// <summary>
 /// Adds the additional headers.
 /// </summary>
 /// <param name="headerName">Name of the header.</param>
 /// <param name="headerValue">The header value.</param>
 protected void AddARequestHeader(string headerName, string headerValue)
 {
     if (ApiHeaders.ContainsKey(headerName))
     {
         ApiHeaders[headerName] = headerValue;
     }
     else
     {
         ApiHeaders.Add(headerName, headerValue);
     }
 }
Exemplo n.º 6
0
#pragma warning disable CA1506
        public async Task <IActionResult> Home(CancellationToken cancellationToken)
        {
            if (controlPanelConfiguration.Enable)
            {
                Response.Headers.Add(
                    HeaderNames.Vary,
                    new StringValues(
                        new[] {
                    ApiHeaders.ApiVersionHeader
                }));
            }

            if (ApiHeaders == null)
            {
                if (controlPanelConfiguration.Enable && !Request.Headers.TryGetValue(ApiHeaders.ApiVersionHeader, out _))
                {
                    Logger.LogDebug("No API headers on request, redirecting to control panel...");
                    return(Redirect(ControlPanelController.ControlPanelRoute));
                }

                try
                {
                    // we only allow authorization header issues
                    var headers = new ApiHeaders(Request.GetTypedHeaders(), true);
                    if (!headers.Compatible())
                    {
                        return(StatusCode(
                                   HttpStatusCode.UpgradeRequired,
                                   new ErrorMessageResponse(ErrorCode.ApiMismatch)));
                    }
                }
                catch (HeadersException)
                {
                    return(HeadersIssue(true));
                }
            }

            return(Json(new ServerInformationResponse
            {
                Version = assemblyInformationProvider.Version,
                ApiVersion = ApiHeaders.Version,
                DMApiVersion = DMApiConstants.InteropVersion,
                MinimumPasswordLength = generalConfiguration.MinimumPasswordLength,
                InstanceLimit = generalConfiguration.InstanceLimit,
                UserLimit = generalConfiguration.UserLimit,
                UserGroupLimit = generalConfiguration.UserGroupLimit,
                ValidInstancePaths = generalConfiguration.ValidInstancePaths,
                WindowsHost = platformIdentifier.IsWindows,
                SwarmServers = swarmService.GetSwarmServers(),
                OAuthProviderInfos = await oAuthProviders.ProviderInfos(cancellationToken).ConfigureAwait(false),
                UpdateInProgress = serverControl.UpdateInProgress,
            }));
        }
        private void ReadConfig()
        {
            Config  = Configuration.Settings;
            BaseUrl = Config.BaseUrl;

            var client = this.GetType().Name.Replace("Client", string.Empty);

            if (Config.Routes.Length > 0 && Config.Routes.Any(r => r.Client.Equals(client, StringComparison.OrdinalIgnoreCase)))
            {
                BaseRoute = Config.Routes.First(r => r.Client.Equals(client, StringComparison.OrdinalIgnoreCase)).Path;
            }
            else
            {
                BaseRoute = Config.DefaultRoute.Replace("[client]", client);
            }

            ApiHeaders.Add("appCode", Config.ClientAppCode);

            //var userName = $@"{Environment.UserDomainName}\{Environment.UserName}";
            //ApiHeaders.Add("clientUser", userName);
        }
Exemplo n.º 8
0
 public void Setup()
 {
     _apiHeaders = new ApiHeaders();
     _apiMethods = new ApiMethods(ROOT_URL, isLogsOn);
     _endpoints  = new Endpoints();
 }
 public void Init()
 {
     apiHeaders = new ApiHeaders();
 }
        /// <inheritdoc />
                #pragma warning disable CA1506 // TODO: Decomplexify
        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // ALL valid token and login requests that match a route go through this function
            // 404 is returned before
            if (AuthenticationContext != null && AuthenticationContext.User == null)
            {
                // valid token, expired password
                await Unauthorized().ExecuteResultAsync(context).ConfigureAwait(false);

                return;
            }

            // validate the headers
            try
            {
                ApiHeaders = new ApiHeaders(Request.GetTypedHeaders());

                if (!ApiHeaders.Compatible())
                {
                    await StatusCode((int)HttpStatusCode.UpgradeRequired, new ErrorMessage
                    {
                        Message = "Provided API version is incompatible with server version!"
                    }).ExecuteResultAsync(context).ConfigureAwait(false);

                    return;
                }

                if (requireInstance)
                {
                    if (!ApiHeaders.InstanceId.HasValue)
                    {
                        await BadRequest(new ErrorMessage { Message = "Missing Instance header!" }).ExecuteResultAsync(context).ConfigureAwait(false);

                        return;
                    }

                    if (AuthenticationContext.InstanceUser == null)
                    {
                        // accessing an instance they don't have access to or one that's disabled
                        await Forbid().ExecuteResultAsync(context).ConfigureAwait(false);

                        return;
                    }
                }
            }
            catch (InvalidOperationException e)
            {
                if (requireHeaders)
                {
                    await BadRequest(new ErrorMessage { Message = e.Message }).ExecuteResultAsync(context).ConfigureAwait(false);

                    return;
                }
            }

            if (ModelState?.IsValid == false)
            {
                var errorMessages = ModelState.SelectMany(x => x.Value.Errors).Select(x => x.ErrorMessage).ToList();

                // HACK
                // do some fuckery to remove RequiredAttribute errors
                for (var I = 0; I < errorMessages.Count; ++I)
                {
                    var message = errorMessages[I];
                    if (message.StartsWith("The ", StringComparison.Ordinal) && message.EndsWith(" field is required.", StringComparison.Ordinal))
                    {
                        errorMessages.RemoveAt(I);
                        --I;
                    }
                }

                if (errorMessages.Count > 0)
                {
                    await BadRequest(new ErrorMessage { Message = String.Join(Environment.NewLine, errorMessages) }).ExecuteResultAsync(context).ConfigureAwait(false);

                    return;
                }
            }

            if (ApiHeaders != null)
            {
                Logger.LogDebug("Request made by User ID {0}. Api version: {1}. User-Agent: {2}. Type: {3}. Route {4}{5} to Instance {6}", AuthenticationContext?.User.Id.ToString(CultureInfo.InvariantCulture), ApiHeaders.ApiVersion, ApiHeaders.RawUserAgent, Request.Method, Request.Path, Request.QueryString, ApiHeaders.InstanceId);
            }

            try
            {
                await base.OnActionExecutionAsync(context, next).ConfigureAwait(false);
            }
            catch (OperationCanceledException e)
            {
                Logger.LogDebug("Request cancelled! Exception: {0}", e);
                throw;
            }
        }
 /// <inheritdoc />
 public IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders) => new ApiClient(new HttpClient(), url, apiHeaders);
        /// <inheritdoc />
        public async Task InjectClaimsIntoContext(TokenValidatedContext tokenValidatedContext, CancellationToken cancellationToken)
        {
            if (tokenValidatedContext == null)
            {
                throw new ArgumentNullException(nameof(tokenValidatedContext));
            }

            // Find the user id in the token
            var userIdClaim = tokenValidatedContext.Principal.FindFirst(JwtRegisteredClaimNames.Sub);

            if (userIdClaim == default)
            {
                throw new InvalidOperationException("Missing required claim!");
            }

            long userId;

            try
            {
                userId = Int64.Parse(userIdClaim.Value, CultureInfo.InvariantCulture);
            }
            catch (Exception e)
            {
                throw new InvalidOperationException("Failed to parse user ID!", e);
            }

            ApiHeaders apiHeaders;

            try
            {
                apiHeaders = new ApiHeaders(tokenValidatedContext.HttpContext.Request.GetTypedHeaders());
            }
            catch (InvalidOperationException)
            {
                // we are not responsible for handling header validation issues
                return;
            }

            // This populates the CurrentAuthenticationContext field for use by us and subsequent controllers
            await authenticationContextFactory.CreateAuthenticationContext(userId, apiHeaders.InstanceId, tokenValidatedContext.SecurityToken.ValidFrom, cancellationToken).ConfigureAwait(false);

            var authenticationContext = authenticationContextFactory.CurrentAuthenticationContext;

            var enumerator = Enum.GetValues(typeof(RightsType));
            var claims     = new List <Claim>();

            foreach (RightsType I in enumerator)
            {
                // if there's no instance user, do a weird thing and add all the instance roles
                // we need it so we can get to OnActionExecutionAsync where we can properly decide between BadRequest and Forbid
                // if user is null that means they got the token with an expired password
                var rightInt  = authenticationContext.User == null || (RightsHelper.IsInstanceRight(I) && authenticationContext.InstanceUser == null) ? ~0U : authenticationContext.GetRight(I);
                var rightEnum = RightsHelper.RightToType(I);
                var right     = (Enum)Enum.ToObject(rightEnum, rightInt);
                foreach (Enum J in Enum.GetValues(rightEnum))
                {
                    if (right.HasFlag(J))
                    {
                        claims.Add(new Claim(ClaimTypes.Role, RightsHelper.RoleName(I, J)));
                    }
                }
            }

            tokenValidatedContext.Principal.AddIdentity(new ClaimsIdentity(claims));
        }
Exemplo n.º 13
0
        /// <summary>
        /// Runs after a <see cref="Token"/> has been validated. Creates the <see cref="IAuthenticationContext"/> for the <see cref="ControllerBase.Request"/>
        /// </summary>
        /// <param name="context">The <see cref="TokenValidatedContext"/> for the operation</param>
        /// <returns>A <see cref="Task"/> representing the running operation</returns>
        public static async Task OnTokenValidated(TokenValidatedContext context)
        {
            var databaseContext = context.HttpContext.RequestServices.GetRequiredService <IDatabaseContext>();
            var authenticationContextFactory = context.HttpContext.RequestServices.GetRequiredService <IAuthenticationContextFactory>();

            var userIdClaim = context.Principal.FindFirst(JwtRegisteredClaimNames.Sub);

            if (userIdClaim == default(Claim))
            {
                throw new InvalidOperationException("Missing required claim!");
            }

            long userId;

            try
            {
                userId = Int64.Parse(userIdClaim.Value, CultureInfo.InvariantCulture);
            }
            catch (Exception e)
            {
                throw new InvalidOperationException("Failed to parse user ID!", e);
            }

            ApiHeaders apiHeaders;

            try
            {
                apiHeaders = new ApiHeaders(context.HttpContext.Request.GetTypedHeaders());
            }
            catch
            {
                //let OnActionExecutionAsync handle the reponse
                return;
            }

            await authenticationContextFactory.CreateAuthenticationContext(userId, apiHeaders.InstanceId, context.SecurityToken.ValidFrom, context.HttpContext.RequestAborted).ConfigureAwait(false);

            var authenticationContext = authenticationContextFactory.CurrentAuthenticationContext;

            var enumerator = Enum.GetValues(typeof(RightsType));
            var claims     = new List <Claim>();

            foreach (RightsType I in enumerator)
            {
                //if there's no instance user, do a weird thing and add all the instance roles
                //we need it so we can get to OnActionExecutionAsync where we can properly decide between BadRequest and Forbid
                //if user is null that means they got the token with an expired password
                var rightInt  = authenticationContext.User == null || (RightsHelper.IsInstanceRight(I) && authenticationContext.InstanceUser == null) ? ~0U : authenticationContext.GetRight(I);
                var rightEnum = RightsHelper.RightToType(I);
                var right     = (Enum)Enum.ToObject(rightEnum, rightInt);
                foreach (Enum J in Enum.GetValues(rightEnum))
                {
                    if (right.HasFlag(J))
                    {
                        claims.Add(new Claim(ClaimTypes.Role, RightsHelper.RoleName(I, J)));
                    }
                }
            }

            context.Principal.AddIdentity(new ClaimsIdentity(claims));
        }
Exemplo n.º 14
0
        /// <inheritdoc />
                #pragma warning disable CA1506 // TODO: Decomplexify
        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            // ALL valid token and login requests that match a route go through this function
            // 404 is returned before
            if (AuthenticationContext != null && AuthenticationContext.User == null)
            {
                // valid token, expired password
                await Unauthorized().ExecuteResultAsync(context).ConfigureAwait(false);

                return;
            }

            // validate the headers
            try
            {
                ApiHeaders = new ApiHeaders(Request.GetTypedHeaders());

                if (!ApiHeaders.Compatible())
                {
                    await StatusCode(
                        (int)HttpStatusCode.UpgradeRequired,
                        new ErrorMessage(ErrorCode.ApiMismatch))
                    .ExecuteResultAsync(context)
                    .ConfigureAwait(false);

                    return;
                }

                if (requireInstance)
                {
                    if (!ApiHeaders.InstanceId.HasValue)
                    {
                        await BadRequest(new ErrorMessage(ErrorCode.InstanceHeaderRequired)).ExecuteResultAsync(context).ConfigureAwait(false);

                        return;
                    }

                    if (AuthenticationContext.InstanceUser == null)
                    {
                        // accessing an instance they don't have access to or one that's disabled
                        await Forbid().ExecuteResultAsync(context).ConfigureAwait(false);

                        return;
                    }
                }
            }
            catch (InvalidOperationException e)
            {
                if (requireHeaders)
                {
                    await BadRequest(
                        new ErrorMessage(ErrorCode.BadHeaders)
                    {
                        AdditionalData = e.Message
                    })
                    .ExecuteResultAsync(context)
                    .ConfigureAwait(false);

                    return;
                }
            }

            if (ModelState?.IsValid == false)
            {
                var errorMessages = ModelState
                                    .SelectMany(x => x.Value.Errors)
                                    .Select(x => x.ErrorMessage)

                                    // We use RequiredAttributes purely for preventing properties from becoming nullable in the databases
                                    // We validate missing required fields in controllers
                                    // Unfortunately, we can't remove the whole validator for that as it checks other things like StringLength
                                    // This is the best way to deal with it unfortunately
                                    .Where(x => !x.EndsWith(" field is required.", StringComparison.Ordinal));

                if (errorMessages.Any())
                {
                    await BadRequest(
                        new ErrorMessage(ErrorCode.ModelValidationFailure)
                    {
                        AdditionalData = String.Join(Environment.NewLine, errorMessages)
                    })
                    .ExecuteResultAsync(context).ConfigureAwait(false);

                    return;
                }

                ModelState.Clear();
            }

            if (ApiHeaders != null)
            {
                Logger.LogDebug(
                    "Request details: User ID {0}. Api version: {1}. User-Agent: {2}. Type: {3}. Route {4}{5} to Instance {6}",
                    AuthenticationContext?.User.Id.Value.ToString(CultureInfo.InvariantCulture),
                    ApiHeaders.ApiVersion.Semver(),
                    ApiHeaders.RawUserAgent,
                    Request.Method,
                    Request.Path,
                    Request.QueryString,
                    ApiHeaders.InstanceId);
            }

            try
            {
                await base.OnActionExecutionAsync(context, next).ConfigureAwait(false);
            }
            catch (OperationCanceledException e)
            {
                Logger.LogDebug("Request cancelled! Exception: {0}", e);
                throw;
            }
        }
Exemplo n.º 15
0
        /// <inheritdoc />
#pragma warning disable CA1506 // TODO: Decomplexify
        public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
        {
            if (context == null)
            {
                throw new ArgumentNullException(nameof(context));
            }

            // ALL valid token and login requests that match a route go through this function
            // 404 is returned before
            if (AuthenticationContext != null && AuthenticationContext.User == null)
            {
                // valid token, expired password
                await Unauthorized().ExecuteResultAsync(context).ConfigureAwait(false);

                return;
            }

            // validate the headers
            try
            {
                ApiHeaders = new ApiHeaders(Request.GetTypedHeaders());

                if (!ApiHeaders.Compatible())
                {
                    await StatusCode(
                        HttpStatusCode.UpgradeRequired,
                        new ErrorMessageResponse(ErrorCode.ApiMismatch))
                    .ExecuteResultAsync(context)
                    .ConfigureAwait(false);

                    return;
                }

                var errorCase = await ValidateRequest(context.HttpContext.RequestAborted).ConfigureAwait(false);

                if (errorCase != null)
                {
                    await errorCase.ExecuteResultAsync(context).ConfigureAwait(false);

                    return;
                }
            }
            catch (HeadersException)
            {
                if (requireHeaders)
                {
                    await HeadersIssue(false)
                    .ExecuteResultAsync(context)
                    .ConfigureAwait(false);

                    return;
                }
            }

            if (ModelState?.IsValid == false)
            {
                var errorMessages = ModelState
                                    .SelectMany(x => x.Value.Errors)
                                    .Select(x => x.ErrorMessage)

                                    // We use RequiredAttributes purely for preventing properties from becoming nullable in the databases
                                    // We validate missing required fields in controllers
                                    // Unfortunately, we can't remove the whole validator for that as it checks other things like StringLength
                                    // This is the best way to deal with it unfortunately
                                    .Where(x => !x.EndsWith(" field is required.", StringComparison.Ordinal));

                if (errorMessages.Any())
                {
                    await BadRequest(
                        new ErrorMessageResponse(ErrorCode.ModelValidationFailure)
                    {
                        AdditionalData = String.Join(Environment.NewLine, errorMessages)
                    })
                    .ExecuteResultAsync(context).ConfigureAwait(false);

                    return;
                }

                ModelState.Clear();
            }

            using (ApiHeaders?.InstanceId != null
                                ? LogContext.PushProperty("Instance", ApiHeaders.InstanceId)
                                : null)
                using (AuthenticationContext != null
                                ? LogContext.PushProperty("User", AuthenticationContext.User.Id)
                                : null)
                    using (LogContext.PushProperty("Request", $"{Request.Method} {Request.Path}"))
                    {
                        if (ApiHeaders != null)
                        {
                            Logger.LogDebug(
                                "Starting API request: Version: {0}. {1}: {2}",
                                ApiHeaders.ApiVersion.Semver(),
                                HeaderNames.UserAgent,
                                ApiHeaders.RawUserAgent);
                        }
                        else if (Request.Headers.TryGetValue(HeaderNames.UserAgent, out var userAgents))
                        {
                            Logger.LogDebug(
                                "Starting unauthorized API request. {0}: {1}",
                                HeaderNames.UserAgent,
                                userAgents);
                        }
                        else
                        {
                            Logger.LogDebug(
                                "Starting unauthorized API request. No {0}!",
                                HeaderNames.UserAgent);
                        }
                        await base.OnActionExecutionAsync(context, next).ConfigureAwait(false);
                    }
        }
Exemplo n.º 16
0
                #pragma warning disable CA1506 // TODO: Decomplexify
        public async Task <IActionResult> CreateToken(CancellationToken cancellationToken)
        {
            if (ApiHeaders == null)
            {
                // Get the exact error
                var errorMessage = "Missing API headers!";
                try
                {
                    var _ = new ApiHeaders(Request.GetTypedHeaders());
                }
                catch (InvalidOperationException ex)
                {
                    errorMessage = ex.Message;
                }

                Response.Headers.Add(HeaderNames.WWWAuthenticate, new StringValues("basic realm=\"Create TGS4 bearer token\""));

                return(BadRequest(
                           new Api.Models.ErrorMessage(Api.Models.ErrorCode.BadHeaders)
                {
                    AdditionalData = errorMessage
                }));
            }

            if (ApiHeaders.IsTokenAuthentication)
            {
                return(BadRequest(new Api.Models.ErrorMessage(Api.Models.ErrorCode.TokenWithToken)));
            }

            ISystemIdentity systemIdentity;

            try
            {
                // trust the system over the database because a user's name can change while still having the same SID
                systemIdentity = await systemIdentityFactory.CreateSystemIdentity(ApiHeaders.Username, ApiHeaders.Password, cancellationToken).ConfigureAwait(false);
            }
            catch (NotImplementedException)
            {
                systemIdentity = null;
            }

            using (systemIdentity)
            {
                // Get the user from the database
                IQueryable <User> query         = DatabaseContext.Users.AsQueryable();
                string            canonicalName = Models.User.CanonicalizeName(ApiHeaders.Username);
                if (systemIdentity == null)
                {
                    query = query.Where(x => x.CanonicalName == canonicalName);
                }
                else
                {
                    query = query.Where(x => x.CanonicalName == canonicalName || x.SystemIdentifier == systemIdentity.Uid);
                }
                var users = await query.Select(x => new User
                {
                    Id           = x.Id,
                    PasswordHash = x.PasswordHash,
                    Enabled      = x.Enabled,
                    Name         = x.Name
                }).ToListAsync(cancellationToken).ConfigureAwait(false);

                // Pick the DB user first
                var user = users
                           .OrderByDescending(dbUser => dbUser.PasswordHash != null)
                           .FirstOrDefault();

                // No user? You're not allowed
                if (user == null)
                {
                    return(Unauthorized());
                }

                // A system user may have had their name AND password changed to one in our DB...
                // Or a DB user was created that had the same user/pass as a system user
                // Dumb admins...
                // FALLBACK TO THE DB USER HERE, DO NOT REVEAL A SYSTEM LOGIN!!!
                // This of course, allows system users to discover TGS users in this (HIGHLY IMPROBABLE) case but that is not our fault
                var  originalHash        = user.PasswordHash;
                var  isDbUser            = originalHash != null;
                bool usingSystemIdentity = systemIdentity != null && !isDbUser;
                if (!usingSystemIdentity)
                {
                    // DB User password check and update
                    if (!cryptographySuite.CheckUserPassword(user, ApiHeaders.Password))
                    {
                        return(Unauthorized());
                    }
                    if (user.PasswordHash != originalHash)
                    {
                        Logger.LogDebug("User ID {0}'s password hash needs a refresh, updating database.", user.Id);
                        var updatedUser = new User
                        {
                            Id = user.Id
                        };
                        DatabaseContext.Users.Attach(updatedUser);
                        updatedUser.PasswordHash = user.PasswordHash;
                        await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
                    }
                }
                else if (systemIdentity.Username != user.Name)
                {
                    // System identity username change update
                    Logger.LogDebug("User ID {0}'s system identity needs a refresh, updating database.", user.Id);
                    DatabaseContext.Users.Attach(user);
                    user.Name          = systemIdentity.Username;
                    user.CanonicalName = Models.User.CanonicalizeName(user.Name);
                    await DatabaseContext.Save(cancellationToken).ConfigureAwait(false);
                }

                // Now that the bookeeping is done, tell them to f**k off if necessary
                if (!user.Enabled.Value)
                {
                    Logger.LogTrace("Not logging in disabled user {0}.", user.Id);
                    return(Forbid());
                }

                var token = await tokenFactory.CreateToken(user, cancellationToken).ConfigureAwait(false);

                if (usingSystemIdentity)
                {
                    // expire the identity slightly after the auth token in case of lag
                    var identExpiry = token.ExpiresAt;
                    identExpiry += tokenFactory.ValidationParameters.ClockSkew;
                    identExpiry += TimeSpan.FromSeconds(15);
                    identityCache.CacheSystemIdentity(user, systemIdentity, identExpiry);
                }

                Logger.LogDebug("Successfully logged in user {0}!", user.Id);

                return(Json(token));
            }
        }
Exemplo n.º 17
0
 /// <inheritdoc />
 public IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders, ApiHeaders?tokenRefreshHeaders) => new ApiClient(new HttpClientImplementation(), url, apiHeaders, tokenRefreshHeaders);
Exemplo n.º 18
0
 /// <inheritdoc />
 public IApiClient CreateApiClient(Uri url, ApiHeaders apiHeaders) => new ApiClient(url, apiHeaders);
Exemplo n.º 19
0
        /// <summary>
        /// Executes the asynchronous.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="action">The action.</param>
        /// <param name="actionTemplate">The action template.</param>
        /// <param name="parameters">The parameters.</param>
        /// <param name="cancellationToken">The cancellation token.</param>
        /// <returns></returns>
        /// <exception cref="ApiClientException">
        /// null
        /// </exception>
        protected async override Task <T> ExecuteAsync <T>(HttpVerb action, string actionTemplate, Parameters parameters,
                                                           CancellationToken cancellationToken = default(CancellationToken))
        {
            var opInfo     = new StackTrace().GetOperationInfo();
            var methodName = opInfo.MetaInfo.Name;
            var urlBuilder = GetRequestUrl(actionTemplate, parameters);

            var client = default(HttpClient);

            try
            {
                using (client = Config.WindowsAuthentication
                    ? new HttpClient(new HttpClientHandler()
                {
                    UseDefaultCredentials = true
                })
                    : new HttpClient())
                {
                    using (var request = new HttpRequestMessage())
                    {
                        //Stuffing header parameters
                        if (parameters?.ContainsKey(BindingSource.Header) ?? false)
                        {
                            parameters[BindingSource.Header]?.ToList().ForEach(p =>
                            {
                                request.Headers.TryAddWithoutValidation(p.Name,
                                                                        ConvertToString(p.Value.ToString(), CultureInfo.InvariantCulture));
                            });
                        }

                        if ((ApiHeaders?.Count ?? 0) > 0)
                        {
                            ApiHeaders?.ToList().ForEach(h =>
                            {
                                request.Headers.TryAddWithoutValidation(h.Key,
                                                                        ConvertToString(h.Value, CultureInfo.InvariantCulture));
                            });
                        }
                        ////Stuffing body parameters
                        //if (parameters?.ContainsKey(BindingSource.Body) ?? false)
                        //{
                        //    if ((parameters[BindingSource.Body]?.Count ?? 0) > 1)
                        //        throw new ApiClientException(
                        //            $"Target of invocation exception in calling {methodName} due to an error: 'API call has multiple parameter bindings to the body element.'");

                        //    var p = parameters[BindingSource.Body]?.ToList().FirstOrDefault();
                        //    var content =
                        //        new StringContent(JsonConvert.SerializeObject(p.Value, _settings.Value));
                        //    content.Headers.ContentType = MediaTypeHeaderValue.Parse("application/json");
                        //    request.Content = content;
                        //}

                        //Binding RequestConent which could be a form / body element
                        if (parameters.RequestContent != null)
                        {
                            request.Content = parameters.RequestContent;
                        }

                        //Setting Http action verb
                        request.Method = new HttpMethod(Enum.GetName(typeof(HttpVerb), action));
                        request.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));

                        PrepareRequest(client, request, urlBuilder);

                        var url = urlBuilder.ToString();

                        request.RequestUri = new Uri(url, UriKind.RelativeOrAbsolute);
                        PrepareRequest(client, request, url);

                        var response = await client
                                       .SendAsync(request, HttpCompletionOption.ResponseHeadersRead, cancellationToken)
                                       .ConfigureAwait(false);

                        try
                        {
                            var headers = response?.Headers.ToDictionary(h => h.Key, h => h.Value);

                            response?.Content?.Headers?.ToList().ForEach(h => { headers[h.Key] = h.Value; });

                            ProcessResponse(client, response);

                            if (response.StatusCode == HttpStatusCode.OK)
                            {
                                var responseData = response.Content == null
                                    ? null
                                    : await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                                var result = default(T);
                                try
                                {
                                    result = JsonConvert.DeserializeObject <T>(responseData, _settings.Value);
                                    return(result);
                                }
                                catch (Exception ex)
                                {
                                    throw new ApiClientException(
                                              $"Target of invocation exception in calling {methodName} due to an error: \"Could not deserialize the response body.\"",
                                              (int)response.StatusCode, responseData, headers, ex);
                                }
                            }
                            else if (response.StatusCode != HttpStatusCode.OK &&
                                     response.StatusCode != HttpStatusCode.NoContent)
                            {
                                var responseData = response.Content == null
                                    ? null
                                    : await response.Content.ReadAsStringAsync().ConfigureAwait(false);

                                throw new ApiClientException(
                                          $"Target of invocation exception in calling {methodName} due to an error: \"The HTTP status code of the response was not expected (\"{Enum.GetName(typeof(HttpStatusCode), response.StatusCode)}:{(int) response.StatusCode}).",
                                          (int)response.StatusCode, responseData, headers, null);
                            }

                            return(default(T));
                        }
                        finally
                        {
                            if (response != null)
                            {
                                response.Dispose();
                            }
                        }
                    }
                }
            }
            finally
            {
                if (client != null)
                {
                    client.Dispose();
                }
            }
        }