src/services/operations/version1/SessionOperationsV1.cs
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Routing;
using Pip.Services.SampleFacade.Clients.Version1;
using PipServices3.Commons.Config;
using PipServices3.Commons.Convert;
using PipServices3.Commons.Data;
using PipServices3.Commons.Errors;
using PipServices3.Commons.Refer;
using PipServices3.Rpc.Services;
using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
namespace Pip.Services.SampleFacade.Operations.Version1
{
public class SessionsOperationsV1 : RestOperations
{
private static ConfigParams _defaultConfig1 = ConfigParams.FromTuples(
"options.cookie_enabled", true,
"options.cookie", "x-session-id",
"options.max_cookie_age", 365L * 24 * 60 * 60 * 1000
);
private string _cookie = "x-session-id";
private bool _cookieEnabled = true;
private long _maxCookieAge = 365L * 24 * 60 * 60 * 1000;
private ISettingsClientV1 _settingsClient;
private IAccountsClientV1 _accountsClient;
private ISessionsClientV1 _sessionsClient;
private IPasswordsClientV1 _passwordsClient;
private IRolesClientV1 _rolesClient;
private IEmailSettingsClientV1 _emailSettingsClient;
private ISitesClientV1 _sitesClient;
private IInvitationsClientV1 _invitationsClient;
public SessionsOperationsV1()
{
_dependencyResolver.Put("settings", new Descriptor("pip-services-settings", "client", "*", "*", "1.0"));
_dependencyResolver.Put("accounts", new Descriptor("pip-services-accounts", "client", "*", "*", "1.0"));
_dependencyResolver.Put("passwords", new Descriptor("pip-services-passwords", "client", "*", "*", "1.0"));
_dependencyResolver.Put("roles", new Descriptor("pip-services-roles", "client", "*", "*", "1.0"));
_dependencyResolver.Put("emailsettings", new Descriptor("pip-services-emailsettings", "client", "*", "*", "1.0"));
_dependencyResolver.Put("sessions", new Descriptor("pip-services-sessions", "client", "*", "*", "1.0"));
_dependencyResolver.Put("sites", new Descriptor("pip-services-sites", "client", "*", "*", "1.0"));
_dependencyResolver.Put("invitations", new Descriptor("pip-services-invitations", "client", "*", "*", "1.0"));
}
public new void Configure(ConfigParams config)
{
config = config.SetDefaults(_defaultConfig1);
_dependencyResolver.Configure(config);
_cookieEnabled = config.GetAsBooleanWithDefault("options.cookie_enabled", _cookieEnabled);
_cookie = config.GetAsStringWithDefault("options.cookie", _cookie);
_maxCookieAge = config.GetAsLongWithDefault("options.max_cookie_age", _maxCookieAge);
base.Configure(config);
}
public new void SetReferences(IReferences references)
{
base.SetReferences(references);
_settingsClient = _dependencyResolver.GetOneRequired<ISettingsClientV1>("settings");
_sessionsClient = _dependencyResolver.GetOneRequired<ISessionsClientV1>("sessions");
_accountsClient = _dependencyResolver.GetOneRequired<IAccountsClientV1>("accounts");
_passwordsClient = _dependencyResolver.GetOneRequired<IPasswordsClientV1>("passwords");
_rolesClient = _dependencyResolver.GetOneRequired<IRolesClientV1>("roles");
_emailSettingsClient = _dependencyResolver.GetOneRequired<IEmailSettingsClientV1>("emailsettings");
_sitesClient = _dependencyResolver.GetOneRequired<ISitesClientV1>("sites");
_invitationsClient = _dependencyResolver.GetOneRequired<IInvitationsClientV1>("invitations");
}
public async Task LoadSessionAsync(HttpRequest request, HttpResponse response, ClaimsPrincipal user, RouteData routeData,
Func<HttpRequest, HttpResponse, ClaimsPrincipal, RouteData, Task> next)
{
var sessionId = request.Headers["x-session-id"];
if (!string.IsNullOrWhiteSpace(sessionId))
{
var session = await _sessionsClient.GetSessionByIdAsync("facade", sessionId);
if (session == null)
{
var error = new UnauthorizedException(
"facade",
"SESSION_NOT_FOUND",
"Session invalid or already expired."
).WithDetails("session_id", sessionId).WithStatus(440);
await SendErrorAsync(response, error);
return;
}
// Associate session user with the request
request.HttpContext.Items["user_id"] = session.UserId;
request.HttpContext.Items["user_name"] = session.UserName;
request.HttpContext.Items["user"] = session.User;
request.HttpContext.Items["session_id"] = session.Id;
// Set IsAuthenticated and add roles to claims
var sessionUser = (SessionUserV1)session.User;
var roles = sessionUser.Roles ?? new List<string>();
var claims = roles.Select(r => new Claim(ClaimTypes.Role, r)).ToList();
// Set user id as claim for OwnerAuthorizer
claims.Add(new Claim("http://schemas.microsoft.com/identity/claims/objectidentifier", session.UserId));
user = new ClaimsPrincipal(new ClaimsIdentity(claims, "Basic"));
}
await next(request, response, user, routeData);
}
public async Task OpenSessionAsync(HttpRequest request, HttpResponse response, AccountV1 account, List<string> roles)
{
try
{
// Retrieve sites for user
var siteIds = roles
.Where(r => r.IndexOf(":") > 0)
.Select(r => r.Substring(0, r.IndexOf(":")))
.ToList();
var sites = new List<SiteV1>();
if (siteIds.Count > 0)
{
var filter = FilterParams.FromTuples("ids", siteIds);
var page = await _sitesClient.GetSitesAsync(null, filter, null);
sites = page.Data;
}
UserPasswordInfoV1 passwordInfo = await _passwordsClient.GetPasswordInfoAsync(null, account.Id);
ConfigParams settings = await _settingsClient.GetSectionByIdAsync(null, account.Id);
// Open a new user session
var user = new SessionUserV1
{
Id = account.Id,
Name = account.Name,
Login = account.Login,
CreateTime = account.CreateTime,
TimeZone = account.TimeZone,
Language = account.Language,
Theme = account.Theme,
Roles = roles,
Sites = sites.Select(s => new SessionSiteV1 { Id = s.Id, Name = s.Name }).ToList<ISessionSite>(),
//Settings: settings,
ChangePwdTime = passwordInfo?.ChangeTime,
CustomHdr = account.CustomHdr,
CustomDat = account.CustomDat
};
string address = null; // HttpRequestDetector.detectAddress(req);
string client = null; // HttpRequestDetector.detectBrowser(req);
//string platform = HttpRequestDetector.detectPlatform(req);
SessionV1 session = await _sessionsClient.OpenSessionAsync(null, account.Id, account.Name, address, client, user, null);
await SendResultAsync(response, session);
}
catch (Exception ex)
{
await SendErrorAsync(response, ex);
}
}
public async Task SignupAsync(HttpRequest request, HttpResponse response)
{
try
{
// Validate password first
// Todo: complete implementation after validate password is added
var parameters = GetParameters(request);
var signupData = JsonConverter.FromJson<SignupData>(parameters.RequestBody);
// Create account
var newAccount = new AccountV1
{
Name = signupData.Name,
Login = signupData.Login ?? signupData.Email, // Use email by default
Language = signupData.Language,
Theme = signupData.Theme,
TimeZone = signupData.TimeZone
};
var account = await _accountsClient.CreateAccountAsync(null, newAccount);
// Create password for the account
var password = signupData.Password;
await _passwordsClient.SetPasswordAsync(
null, account.Id, password);
// Activate all pending invitations
var roles = new List<string>();
var email = signupData.Email;
var invitations = await _invitationsClient.ActivateInvitationsAsync(null, email, account.Id);
var invited = false;
if (invitations != null)
{
// Calculate user roles from activated invitations
foreach (var invitation in invitations)
{
// Was user invited with the same email?
invited = invited || email == invitation.InviteeEmail;
if (invitation.SiteId != null)
{
invitation.Role ??= "user";
var role = invitation.SiteId + ':' + invitation.Role;
roles.Add(role);
}
}
}
// Create email settings for the account
var newEmailSettings = new EmailSettingsV1
{
Id = account.Id,
Name = account.Name,
Email = email,
Language = account.Language
};
if (_emailSettingsClient != null)
{
if (invited)
{
await _emailSettingsClient.SetVerifiedSettingsAsync(null, newEmailSettings);
}
else
{
await _emailSettingsClient.SetSettingsAsync(null, newEmailSettings);
}
}
await OpenSessionAsync(request, response, account, roles);
}
catch (Exception ex)
{
await SendErrorAsync(response, ex);
}
}
public async Task SignupValidateAsync(HttpRequest request, HttpResponse response)
{
var parameters = GetParameters(request);
var login = parameters.GetAsNullableString("login");
Exception err = null;
if (login != null)
{
try
{
var account = await _accountsClient.GetAccountByIdOrLoginAsync(null, login);
if (account != null)
{
err = new BadRequestException(
null, "LOGIN_ALREADY_USED",
"Login " + login + " already being used"
).WithDetails("login", login);
}
}
catch (Exception ex)
{
err = ex;
}
}
if (err != null) await SendErrorAsync(response, err);
else await SendEmptyResultAsync(response);
}
public async Task SigninAsync(HttpRequest request, HttpResponse response)
{
var parameters = GetParameters(request);
var login = parameters.GetAsNullableString("login");
var password = parameters.GetAsNullableString("password");
try
{
// Find user account
var account = await _accountsClient.GetAccountByIdOrLoginAsync(null, login);
if (account == null)
{
await SendErrorAsync(response, new BadRequestException(
null,
"WRONG_LOGIN",
"Account " + login + " was not found"
).WithDetails("login", login));
return;
}
// Authenticate user
var result = false;
try
{
result = await _passwordsClient.AuthenticateAsync(null, account.Id, password);
}
catch (Exception ex)
{
if (!ex.Message.Contains("Invalid password"))
throw;
}
if (!result)
{
await SendErrorAsync(response, new BadRequestException(
null,
"WRONG_PASSWORD",
"Wrong password for account " + login
).WithDetails("login", login));
return;
}
// Retrieve user roles
var roles = new List<string>();
if (_rolesClient != null)
{
var data = await _rolesClient.GetRolesByIdAsync(null, account.Id);
if (data != null)
{
roles.AddRange(data);
}
}
await OpenSessionAsync(request, response, account, roles);
}
catch (Exception ex)
{
await SendErrorAsync(response, ex);
}
}
public async Task SignoutAsync(HttpRequest request, HttpResponse response)
{
// Cleanup cookie with session id
// if (_cookieEnabled)
// response.ClearCookie(_cookie);
var sessionId = GetContextItem<string>(request, "session_id");
try
{
if (sessionId != null)
{
await _sessionsClient.CloseSessionAsync(null, sessionId);
}
await SendEmptyResultAsync(response);
}
catch (Exception ex)
{
await SendErrorAsync(response, ex);
}
}
public async Task GetSessionsAsync(HttpRequest request, HttpResponse response)
{
try
{
var filter = GetFilterParams(request);
var paging = GetPagingParams(request);
var page = await _sessionsClient.GetSessionsAsync(null, filter, paging);
await SendResultAsync(response, page);
}
catch (Exception ex)
{
await SendErrorAsync(response, ex);
}
}
public async Task RestoreSessionAsync(HttpRequest request, HttpResponse response)
{
try
{
var parameters = GetParameters(request);
var sessionId = parameters.GetAsNullableString("session_id");
var session = await _sessionsClient.GetSessionByIdAsync(null, sessionId);
// If session closed then return null
if (session != null && !session.Active)
session = null;
if (session == null) await SendEmptyResultAsync(response);
else await SendResultAsync(response, session);
}
catch (Exception ex)
{
await SendErrorAsync(response, ex);
}
}
public async Task GetUserSessionsAsync(HttpRequest request, HttpResponse response)
{
try
{
var filter = GetFilterParams(request);
var paging = GetPagingParams(request);
var parameters = GetParameters(request);
var userId =
parameters.GetAsNullableString("user_id") ??
parameters.GetAsNullableString("account_id");
filter.SetAsObject("user_id", userId);
var page = await _sessionsClient.GetSessionsAsync(null, filter, paging);
await SendResultAsync(response, page);
}
catch (Exception ex)
{
await SendErrorAsync(response, ex);
}
}
public async Task GetCurrentSessionAsync(HttpRequest request, HttpResponse response)
{
try
{
// parse headers first, and if nothing in headers get cookie
var sessionId = request.Headers["x-session-id"];
var session = await _sessionsClient.GetSessionByIdAsync(null, sessionId);
await SendResultAsync(response, session);
}
catch (Exception ex)
{
await SendErrorAsync(response, ex);
}
}
public async Task CloseSessionAsync(HttpRequest request, HttpResponse response)
{
try
{
var parameters = GetParameters(request);
var sessionId =
parameters.GetAsNullableString("session_id");
var session = await _sessionsClient.CloseSessionAsync(null, sessionId);
await SendResultAsync(response, session);
}
catch (Exception ex)
{
await SendErrorAsync(response, ex);
}
}
private static new T GetContextItem<T>(HttpRequest request, string name)
where T : class
{
if (request != null && request.HttpContext.Items.TryGetValue(name, out object item))
{
return item as T;
}
return null;
}
}
}