From e808966f9fd6c508587e6e47f8cf3091fffdac51 Mon Sep 17 00:00:00 2001 From: Hamid Reza Mohammadi Date: Tue, 20 Oct 2020 19:07:52 +0330 Subject: [PATCH] #22 API for session renewal --- .../Controllers/AppUserControllerBase.cs | 24 ++++++ RSecurityBackend/RSecurityBackend.xml | 23 ++++++ RSecurityBackend/Services/IAppUserService.cs | 8 ++ .../Services/Implementation/AppUserService.cs | 74 ++++++++++++++++++- 4 files changed, 126 insertions(+), 3 deletions(-) diff --git a/RSecurityBackend/Controllers/AppUserControllerBase.cs b/RSecurityBackend/Controllers/AppUserControllerBase.cs index f1ff7643..fff412b0 100644 --- a/RSecurityBackend/Controllers/AppUserControllerBase.cs +++ b/RSecurityBackend/Controllers/AppUserControllerBase.cs @@ -53,6 +53,30 @@ namespace RSecurityBackend.Controllers return Ok(res.Result); } + /// + /// renew an expired session + /// + /// user session id + /// LoggedOnUserModel + [HttpPut] + [AllowAnonymous] + [Route("relogin/{sessionId}")] + [ProducesResponseType((int)HttpStatusCode.OK, Type = typeof(LoggedOnUserModel))] + [ProducesResponseType((int)HttpStatusCode.BadRequest, Type = typeof(string))] + public async Task ReLogin( + Guid sessionId + ) + { + string clientIPAddress = _httpContextAccessor.HttpContext.Connection.RemoteIpAddress.ToString(); + RServiceResult res = await _appUserService.ReLogin(sessionId, clientIPAddress); + if (res.Result == null) + { + return BadRequest(res.ExceptionString); + } + + return Ok(res.Result); + } + /// /// Logout user (users need user:delothersession to logout other users) /// diff --git a/RSecurityBackend/RSecurityBackend.xml b/RSecurityBackend/RSecurityBackend.xml index 1c6f20f6..00a512cd 100644 --- a/RSecurityBackend/RSecurityBackend.xml +++ b/RSecurityBackend/RSecurityBackend.xml @@ -70,6 +70,13 @@ loginViewModel LoggedOnUserModel + + + renew an expired session + + user session id + LoggedOnUserModel + Logout user (users need user:delothersession to logout other users) @@ -1820,6 +1827,14 @@ + + + replace a (probably expired session) with a new one + + + + + Logout @@ -2131,6 +2146,14 @@ + + + replace a (probably expired session) with a new one + + + + + add user to role diff --git a/RSecurityBackend/Services/IAppUserService.cs b/RSecurityBackend/Services/IAppUserService.cs index 70915456..6796110a 100644 --- a/RSecurityBackend/Services/IAppUserService.cs +++ b/RSecurityBackend/Services/IAppUserService.cs @@ -24,6 +24,14 @@ namespace RSecurityBackend.Services /// Task> Login(LoginViewModel loginViewModel, string clientIPAddress); + /// + /// replace a (probably expired session) with a new one + /// + /// + /// + /// + Task> ReLogin(Guid sessionId, string clientIPAddress); + /// /// Logout /// diff --git a/RSecurityBackend/Services/Implementation/AppUserService.cs b/RSecurityBackend/Services/Implementation/AppUserService.cs index b56e7f96..74b859f9 100644 --- a/RSecurityBackend/Services/Implementation/AppUserService.cs +++ b/RSecurityBackend/Services/Implementation/AppUserService.cs @@ -16,7 +16,6 @@ using RSecurityBackend.Models.Auth.Db; using RSecurityBackend.Models.Auth.ViewModels; using RSecurityBackend.Models.Generic; using RSecurityBackend.DbContext; -using RSecurityBackend.Utilities; using RSecurityBackend.Models.Image; using RSecurityBackend.Models.Auth.Memory; using RSecurityBackend.Models.Audit.Db; @@ -28,7 +27,7 @@ namespace RSecurityBackend.Services.Implementation /// Authentication Service /// public class AppUserService : IAppUserService - { + { /// /// Login user, if failed return LoggedOnUserModel is null @@ -41,7 +40,7 @@ namespace RSecurityBackend.Services.Implementation try { - //we ignore loginViewModel in automatic auditing to prevent loginng password data, so we would add a manual auditing to have enough data on login intrusion and ... + //we ignore loginViewModel in automatic auditing to prevent logging password data, so we would add a manual auditing to have enough data on login intrusion and ... REvent log = new REvent() { EventType = "AppUser/Login (POST)(Manual)", @@ -130,6 +129,75 @@ namespace RSecurityBackend.Services.Implementation } } + /// + /// replace a (probably expired session) with a new one + /// + /// + /// + /// + public async Task> ReLogin(Guid sessionId, string clientIPAddress) + { + try + { + RTemporaryUserSession oldSession = await _context.Sessions.Include(s => s.RAppUser).Where(s => s.Id == sessionId).SingleOrDefaultAsync(); + if (oldSession == null) + { + return new RServiceResult(null, "Invalid session"); + } + RAppUser appUser = oldSession.RAppUser; + if (appUser.Status == RAppUserStatus.Inactive) + { + return new RServiceResult(null, "User is disabled by an admin."); + } + RServiceResult securableItems = await GetUserSecurableItemsStatus(appUser.Id); + if (!string.IsNullOrEmpty(securableItems.ExceptionString)) + return new RServiceResult(null, securableItems.ExceptionString); + + RTemporaryUserSession newSession = + new RTemporaryUserSession() + { + RAppUserId = appUser.Id, + ClientIPAddress = clientIPAddress, + ClientAppName = oldSession.ClientAppName, + Language = oldSession.Language, + LoginTime = DateTime.Now, + LastRenewal = DateTime.Now, + ValidUntil = DateTime.Now + TimeSpan.FromSeconds(DefaultTokenExpirationInSeconds), + Token = "" + }; + + + await _context.Sessions.AddAsync(newSession); + _context.Sessions.Remove(oldSession); + + await _context.SaveChangesAsync(); + + RServiceResult userToken = await GenerateToken(appUser.UserName, appUser.Id, newSession.Id); + if (userToken.Result == null) + { + return new RServiceResult(null, userToken.ExceptionString); + } + newSession.Token = userToken.Result; + _context.Sessions.Update(newSession); + _context.SaveChanges(); + + return + new RServiceResult( + new LoggedOnUserModel() + { + SessionId = newSession.Id, + User = new PublicRAppUser(appUser), + Token = userToken.Result, + SecurableItem = securableItems.Result + } + ); + } + catch (Exception exp) + { + return new RServiceResult(null, exp.ToString()); + } + } + /// /// add user to role ///