Asp.Net Web Api Task тупик - PullRequest
       19

Asp.Net Web Api Task тупик

0 голосов
/ 27 июня 2018

Я разрабатываю тестовый проект на основе ASP.NET Identity проекта с открытым исходным кодом В конкретном случае у меня есть проект ASP.Net Web Api, в котором я расширил CookieAuthenticationProvider и переопределил метод ValidateIdentity.

Это класс, который я расширил

public sealed class MeteoraInternalCookieAuthenticationProvider : CookieAuthenticationProvider
{
    private readonly TimeSpan _validateInterval;

    public MeteoraInternalCookieAuthenticationProvider(TimeSpan validateInterval)
        : base()
    {
        _validateInterval = validateInterval;
    }

    public override async Task ValidateIdentity(CookieValidateIdentityContext context)
    {
        var currentUtc = DateTimeOffset.UtcNow;
        if (context.Options != null && context.Options.SystemClock != null)
        {
            currentUtc = context.Options.SystemClock.UtcNow;
        }
        var issuedUtc = context.Properties.IssuedUtc;

        // Only validate if enough time has elapsed
        var validate = (issuedUtc == null);
        if (issuedUtc != null)
        {
            var timeElapsed = currentUtc.Subtract(issuedUtc.Value);
            validate = timeElapsed > _validateInterval;
        }
        if (validate)
        {
            var manager = context.OwinContext.Get<UserManager>();
            var userId = context.Identity.GetUserId<Guid>();
            var app = await this.GetApplicationContextForValidateIdentity(context.OwinContext.Get<Guid>(MeteoraOwinSetsKeys.ApplicationId).ToString());

            if (manager != null && userId != null)
            {
                var user = manager.FindById(userId);
                var reject = true;
                //Refresh the identity if the stamp matches, otherwise reject
                if (user != null && manager.SupportsUserSecurityStamp)
                {
                    var securityStamp = context.Identity.FindFirstValue(MeteoraClaimTypes.SecurityStampClaimType);
                    if (securityStamp == manager.GetSecurityStamp(userId))
                    {
                        reject = false;
                        // Regenerate fresh claims if possible and resign in

                        var identity = await this.RegenerateIdentity(manager, user, app);
                        /*
                            Other Code
                        */

                    }
                }
                if (reject)
                {
                    context.RejectIdentity();
                    context.OwinContext.Authentication.SignOut(context.Options.AuthenticationType);
                }
            }
        }

    }

    private async Task<ClaimsIdentity> RegenerateIdentity(UserManager usrMgr, IdentityUser usr, IdentityApplication app)
    {
        ClaimsIdentity identity = await usrMgr.CreateIdentityAsync(usr, app, OAuthDefaults.AuthenticationType /* default is "Bearer" */).WithCurrentCulture();
        identity.AddClaim(new IdentityClaim(MeteoraClaimTypes.ApplicationIdType, app.Id.ToString()));
        return identity;
    }

    /*
        Other Methods
    */
}

Это приложение размещено в IIS, и когда я делаю запрос, вызывается метод ValidateIdentity. Тогда внутри вызывается метод RegenerateIdentity. RegenerateIdentity использует класс UserManager для генерации нового ClaimsIdentity.

Здесь есть весь код

public class UserManager<TUser, TApplication, ... >
{
    public virtual async Task<ClaimsIdentity> CreateIdentityAsync(TUser user, TApplication app, string authenticationType)
    {
        /* Other Code */

        return await ClaimsIdentityFactory.CreateAsync(this, user, app, authenticationType);
    }

    public virtual async Task<IList<string>> GetRoleNamesAsync(TApplication app, TUser user)
    {
        /* Other Code */

        var userRoleStore = GetUserRoleStore();
        return await userRoleStore.GetRoleNamesAsync(app, user).WithCurrentCulture();
    }
}

public class ClaimsIdentityFactory<TUser, TApplication, TRole, ... >
{
    public virtual async Task<ClaimsIdentity> CreateAsync(UserManager<TUser, TApplication, ...> manager, TUser user, TApplication app, string authenticationType)
    {
        /* Other Code */

        ClaimsIdentity cid = new ClaimsIdentity(authenticationType, UserNameClaimType, RoleClaimType);

        cid.AddClaim(new Claim(UserIdClaimType, ConvertIdToString(user.Id), ClaimValueTypes.String));
        cid.AddClaim(new Claim(UserNameClaimType, user.UserName, ClaimValueTypes.String));
        cid.AddClaim(new Claim(IdentityProviderClaimType, DefaultIdentityProviderClaimValue, ClaimValueTypes.String));

        /* Other code */

        if (manager.SupportsUserRole)
        {
            IList<string> roles = await manager.GetRoleNamesAsync(app, user); // *** CALLING THIS METHOD ***
            foreach (string roleName in roles)
                cid.AddClaim(new Claim(RoleClaimType, roleName, ClaimValueTypes.String));
        }

        /* Other Code */

        return cid;
    }
}


public abstract class UserStore <TKey, TUser, TApplication ...>
{
        public virtual async Task<IList<string>> GetRoleNamesAsync(TApplication app, TUser user)
        {
            /* Other Code */
            return await this.GetRoleNamesAsync(app.Id, user.Id);
        }

        public virtual async Task<IList<string>> GetRoleNamesAsync(TKey appId, TKey userId)
        {
            /* Other Code */

            var query = from userAppRole in _userApplicationRoles
                        where userAppRole.User.Id.Equals(userId) && userAppRole.Application.Id.Equals(appId)
                        join role in _roleStore.DbEntitySet on userAppRole.Role.Id equals role.Id
                        select role.InvariantName;

            return await query.ToListAsync(); // *** DEADLOCK ????? ***
        }
}

Сначала называется метод ClaimsIdentityFactory.CreateAsync. Внутри ClaimsIdentityFactory class CreateAsync метод называется manager.GetRoleNamesAsync, который при использовании хранилища на основе EntityFramework вызывает метод userRoleStore.GetRoleNamesAsync. В классе UserStore я, кажется, экспериментирую с какой-то тупиковой ситуацией после вызова return await query.ToListAsync();, потому что метод никогда не возвращается.

Эта проблема отсутствует в моем проекте UnitTest, но возникает в среде IIS. Что я могу сделать, чтобы понять, что происходит на самом деле?

...