예제 #1
0
    protected virtual async Task <SessionInfo[]> GetUserSessions(
        string userId, CancellationToken cancellationToken = default)
    {
        if (!DbUserIdHandler.TryParse(userId).IsSome(out var dbUserId))
        {
            return(Array.Empty <SessionInfo>());
        }

        var dbContext = CreateDbContext();

        await using var _1 = dbContext.ConfigureAwait(false);
        var tx = await dbContext.Database.BeginTransactionAsync(cancellationToken).ConfigureAwait(false);

        await using var _2 = tx.ConfigureAwait(false);

        var dbSessions = await Sessions.ListByUser(dbContext, dbUserId, cancellationToken).ConfigureAwait(false);

        var sessions = new SessionInfo[dbSessions.Length];

        for (var i = 0; i < dbSessions.Length; i++)
        {
            sessions[i] = SessionConverter.ToModel(dbSessions[i]) !;
        }
        return(sessions);
    }
예제 #2
0
    // [CommandHandler] inherited
    public override async Task SetOptions(
        SetSessionOptionsCommand command, CancellationToken cancellationToken = default)
    {
        var(session, options, baseVersion) = command;
        var context = CommandContext.GetCurrent();

        if (Computed.IsInvalidating())
        {
            _ = GetSessionInfo(session, default);
            _ = GetOptions(session, default);
            return;
        }

        var dbContext = await CreateCommandDbContext(cancellationToken).ConfigureAwait(false);

        await using var _1 = dbContext.ConfigureAwait(false);

        var dbSessionInfo = await Sessions.Get(dbContext, session.Id, true, cancellationToken).ConfigureAwait(false);

        var sessionInfo = SessionConverter.ToModel(dbSessionInfo);

        if (sessionInfo == null)
        {
            throw new KeyNotFoundException();
        }
        if (baseVersion.HasValue && sessionInfo.Version != baseVersion.GetValueOrDefault())
        {
            throw new VersionMismatchException();
        }

        sessionInfo = sessionInfo with {
            LastSeenAt = Clocks.SystemClock.Now,
            Options    = options,
        };
        await Sessions.Upsert(dbContext, sessionInfo, cancellationToken).ConfigureAwait(false);
    }
예제 #3
0
    // [CommandHandler] inherited
    public override async Task <SessionInfo> SetupSession(
        SetupSessionCommand command, CancellationToken cancellationToken = default)
    {
        var(session, ipAddress, userAgent) = command;
        var context = CommandContext.GetCurrent();

        if (Computed.IsInvalidating())
        {
            var invSessionInfo = context.Operation().Items.Get <SessionInfo>();
            if (invSessionInfo == null)
            {
                return(null !);
            }
            _ = GetSessionInfo(session, default);
            var invIsNew = context.Operation().Items.GetOrDefault(false);
            if (invIsNew)
            {
                _ = GetAuthInfo(session, default);
                _ = GetOptions(session, default);
            }
            if (invSessionInfo.IsAuthenticated)
            {
                _ = GetUserSessions(invSessionInfo.UserId, default);
            }
            return(null !);
        }

        var dbContext = await CreateCommandDbContext(cancellationToken).ConfigureAwait(false);

        await using var _1 = dbContext.ConfigureAwait(false);

        var dbSessionInfo = await Sessions.Get(dbContext, session.Id, true, cancellationToken).ConfigureAwait(false);

        var isNew = dbSessionInfo == null;
        var now   = Clocks.SystemClock.Now;
        var sessionInfo = SessionConverter.ToModel(dbSessionInfo)
                          ?? SessionConverter.NewModel() with {
            Id = session.Id
        };

        sessionInfo = sessionInfo with {
            LastSeenAt = now,
            IPAddress  = string.IsNullOrEmpty(ipAddress) ? sessionInfo.IPAddress : ipAddress,
            UserAgent  = string.IsNullOrEmpty(userAgent) ? sessionInfo.UserAgent : userAgent,
        };
        try {
            dbSessionInfo = await Sessions.Upsert(dbContext, sessionInfo, cancellationToken).ConfigureAwait(false);

            sessionInfo = SessionConverter.ToModel(dbSessionInfo);
            context.Operation().Items.Set(sessionInfo); // invSessionInfo
            context.Operation().Items.Set(isNew);       // invIsNew
            return(sessionInfo !);
        }
        catch (DbUpdateException) {
            var   scope = context.Items.Get <DbOperationScope <TDbContext> >();
            await scope !.Rollback().ConfigureAwait(false);

            var readDbContext = CreateDbContext();
            await using var __ = readDbContext.ConfigureAwait(false);

            dbSessionInfo = await Sessions.Get(readDbContext, session.Id, false, cancellationToken).ConfigureAwait(false);

            if (dbSessionInfo == null)
            {
                throw; // Something is off: it is supposed to be created concurrently
            }
            sessionInfo = SessionConverter.ToModel(dbSessionInfo);
            return(sessionInfo !);
        }
    }
예제 #4
0
    // Commands

    // [CommandHandler] inherited
    public override async Task SignIn(
        SignInCommand command, CancellationToken cancellationToken = default)
    {
        var(session, user, authenticatedIdentity) = command;
        var context = CommandContext.GetCurrent();

        if (Computed.IsInvalidating())
        {
            _ = GetAuthInfo(session, default);
            _ = GetSessionInfo(session, default);
            var invSessionInfo = context.Operation().Items.Get <SessionInfo>();
            if (invSessionInfo != null)
            {
                _ = GetUser(invSessionInfo.UserId, default);
                _ = GetUserSessions(invSessionInfo.UserId, default);
            }
            return;
        }

        if (!user.Identities.ContainsKey(authenticatedIdentity))
#pragma warning disable MA0015
        {
            throw new ArgumentOutOfRangeException(
                      $"{nameof(command)}.{nameof(SignInCommand.AuthenticatedIdentity)}");
        }
#pragma warning restore MA0015
        if (await IsSignOutForced(session, cancellationToken).ConfigureAwait(false))
        {
            throw Errors.ForcedSignOut();
        }

        var dbContext = await CreateCommandDbContext(cancellationToken).ConfigureAwait(false);

        await using var _1 = dbContext.ConfigureAwait(false);

        var isNewUser = false;
        var dbUser    = await Users
                        .GetByUserIdentity(dbContext, authenticatedIdentity, cancellationToken)
                        .ConfigureAwait(false);

        if (dbUser == null)
        {
            (dbUser, isNewUser) = await Users
                                  .GetOrCreateOnSignIn(dbContext, user, cancellationToken)
                                  .ConfigureAwait(false);

            if (isNewUser == false)
            {
                UserConverter.UpdateEntity(user, dbUser);
                await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
            }
        }
        else
        {
            user = user with {
                Id = DbUserIdHandler.Format(dbUser.Id)
            };
            UserConverter.UpdateEntity(user, dbUser);
            await dbContext.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
        }

        var dbSessionInfo = await Sessions.GetOrCreate(dbContext, session.Id, cancellationToken).ConfigureAwait(false);

        var sessionInfo = SessionConverter.ToModel(dbSessionInfo);
        if (sessionInfo !.IsSignOutForced)
        {
            throw Errors.ForcedSignOut();
        }

        sessionInfo = sessionInfo with {
            LastSeenAt            = Clocks.SystemClock.Now,
            AuthenticatedIdentity = authenticatedIdentity,
            UserId = DbUserIdHandler.Format(dbUser.Id)
        };
        context.Operation().Items.Set(sessionInfo);
        context.Operation().Items.Set(isNewUser);
        await Sessions.Upsert(dbContext, sessionInfo, cancellationToken).ConfigureAwait(false);
    }