In most cases, access to an application’s (service’s) resources is granted only after users authenticate themselves in the system. Authentication is the process of checking the validity of the identifier provided by a user. A successful authentication (besides establishing a trusted relationship and generating a session key) is usually also followed up by user authorization. This second step grants the user access rights to an approved set of resources, deemed necessary for the user to perform his/her tasks.
Just like in the previous step, we’ll be placing the files of this step in the operation/version1 folder.
Let’s start by defining a data model for storing user information within a session. Create a new file named SessionUserV1.py with the following code:
src/operations/version1/SessionUserV1.ts
export class SessionUserV1 {
/* Identification */
public id: string;
public login: string;
public name: string;
public create_time: Date;
/* User information */
public time_zone: string;
public language: string;
public theme: string;
/* Security info **/
public roles: string[];
public change_pwd_time: Date;
public sites: [{ id: string, name: string }];
public settings: any;
/* Custom fields */
public custom_hdr: any;
public custom_dat: any;
}
src/service/operations/version1/SessionUserV1.cs
using System;
using System.Collections.Generic;
using System.Runtime.Serialization;
using System.Text;
namespace Pip.Services.SampleFacade.Operations.Version1
{
[DataContract]
public class SessionUserV1
{
// Identification
[DataMember(Name = "id")]
public string Id { get; set; }
[DataMember(Name = "login")]
public string Login { get; set; }
[DataMember(Name = "name")]
public string Name { get; set; }
[DataMember(Name = "create_time")]
public DateTime CreateTime { get; set; }
// User preferences
[DataMember(Name = "time_zone")]
public string TimeZone { get; set; }
[DataMember(Name = "language")]
public string Language { get; set; }
[DataMember(Name = "theme")]
public string Theme { get; set; }
// Security info
[DataMember(Name = "roles")]
public List<string> Roles { get; set; }
[DataMember(Name = "change_pwd_time")]
public DateTime? ChangePwdTime { get; set; }
[DataMember(Name = "sites")]
public List<ISessionSite> Sites { get; set; }
[DataMember(Name = "settings")]
public Dictionary<string, string> Settings { get; set; }
// Custom fields
[DataMember(Name = "custom_hdr")]
public object CustomHdr { get; set; }
[DataMember(Name = "custom_dat")]
public object CustomDat { get; set; }
}
}
/operations/version1/SessionUserV1.go
package operations1
import (
"time"
)
type SessionUserV1 struct {
/* Identification */
Id string `json:"id"`
Login string `json:"login"`
Name string `json:"name"`
CreateTime time.Time `json:"create_time"`
/* User information */
TimeZone string `json:"tim_zone"`
Language string `json:"language"`
Theme string `json:"theme"`
/* Security info **/
Roles []string `json:"roles"`
ChangePwdTime time.Time `json:"change_pwd_time"`
Sites Site `json:"sites"`
Settings interface{} `json:"settings"`
/* Custom fields */
CustomHdr interface{} `json:"custom_hdr"`
CustomDat interface{} `json:"custom_dat"`
}
type Site struct {
Id string `json:"id"`
Name string `json:"name"`
}
pip_facades_sample_python/operations/version1/SessionUserV1.py
# -*- coding: utf-8 -*-
import datetime
from typing import List, Any
class SessionUserV1:
def __init__(self, id: str = None, login: str = None, name: str = None, create_time: str = None,
time_zone: str = None, language: str = None,
theme: str = None, roles: List[str] = None, change_pwd_time: datetime.datetime = None,
sites: List[dict] = None, settings: Any = None, custom_hdr: Any = None,
custom_dat: Any = None):
# Identification
self.id = id
self.login = login
self.name = name
self.create_time = create_time
# User information
self.time_zone = time_zone
self.language = language
self.theme = theme
# Security info
self.roles = roles
self.change_pwd_time = change_pwd_time
self.sites = sites
self.settings = settings
# Custom fields
self.custom_hdr = custom_hdr
self.custom_dat = custom_dat
This data model will contain all necessary information about the user: the session’s ID, login, username, list of rolls, etc.
We’ll be defining our operations for managing sessions and authenticating users in a file named SessionOperationsV1.py. A listing of this file’s code is presented below:
src/operations/version1/SessionOperationsV1.ts
import { ConfigParams } from 'pip-services3-commons-nodex';
import { FilterParams } from 'pip-services3-commons-nodex';
import { IReferences } from 'pip-services3-commons-nodex';
import { Descriptor } from 'pip-services3-commons-nodex';
import { BadRequestException } from 'pip-services3-commons-nodex';
import { UnauthorizedException } from 'pip-services3-commons-nodex';
import { HttpRequestDetector } from 'pip-services3-rpc-nodex';
import { RestOperations } from 'pip-services3-rpc-nodex';
import { ISettingsClientV1 } from '../../clients/version1/ISettingsClientV1';
import { IAccountsClientV1 } from '../../clients/version1/IAccountsClientV1';
import { AccountV1 } from '../../clients/version1/AccountV1';
import { IPasswordsClientV1 } from '../../clients/version1/IPasswordsClientV1';
import { UserPasswordInfoV1 } from '../../clients/version1/UserPasswordInfoV1';
import { IRolesClientV1 } from '../../clients/version1/IRolesClientV1';
import { ISessionsClientV1 } from '../../clients/version1/ISessionsClientV1';
import { SessionV1 } from '../../clients/version1/SessionV1';
import { IEmailSettingsClientV1 } from '../../clients/version1/IEmailSettingsClientV1';
import { EmailSettingsV1 } from '../../clients/version1/EmailSettingsV1';
import { ISitesClientV1 } from '../../clients/version1/ISitesClientV1';
import { IInvitationsClientV1 } from '../../clients/version1/IInvitationsClientV1';
import { SiteV1 } from '../../clients/version1/SiteV1';
import { SessionUserV1 } from './SessionUserV1';
export class SessionsOperationsV1 extends RestOperations {
private static _defaultConfig1 = ConfigParams.fromTuples(
'options.cookie_enabled', true,
'options.cookie', 'x-session-id',
'options.max_cookie_age', 365 * 24 * 60 * 60 * 1000
);
private _cookie: string = 'x-session-id';
private _cookieEnabled: boolean = true;
private _maxCookieAge: number = 365 * 24 * 60 * 60 * 1000;
private _settingsClient: ISettingsClientV1;
private _accountsClient: IAccountsClientV1;
private _sessionsClient: ISessionsClientV1;
private _passwordsClient: IPasswordsClientV1;
private _rolesClient: IRolesClientV1;
private _emailSettingsClient: IEmailSettingsClientV1;
private _sitesClient: ISitesClientV1;
private _invitationsClient: IInvitationsClientV1;
public constructor() {
super();
this._dependencyResolver.put('settings', new Descriptor('pip-services-settings', 'client', '*', '*', '1.0'));
this._dependencyResolver.put('accounts', new Descriptor('pip-services-accounts', 'client', '*', '*', '1.0'));
this._dependencyResolver.put('passwords', new Descriptor('pip-services-passwords', 'client', '*', '*', '1.0'));
this._dependencyResolver.put('roles', new Descriptor('pip-services-roles', 'client', '*', '*', '1.0'));
this._dependencyResolver.put('emailsettings', new Descriptor('pip-services-emailsettings', 'client', '*', '*', '1.0'));
this._dependencyResolver.put('sessions', new Descriptor('pip-services-sessions', 'client', '*', '*', '1.0'));
this._dependencyResolver.put('sites', new Descriptor('pip-services-sites', 'client', '*', '*', '1.0'));
this._dependencyResolver.put('invitations', new Descriptor('pip-services-invitations', 'client', '*', '*', '1.0'));
}
public configure(config: ConfigParams): void {
config = config.setDefaults(SessionsOperationsV1._defaultConfig1);
this._dependencyResolver.configure(config);
this._cookieEnabled = config.getAsBooleanWithDefault('options.cookie_enabled', this._cookieEnabled);
this._cookie = config.getAsStringWithDefault('options.cookie', this._cookie);
this._maxCookieAge = config.getAsLongWithDefault('options.max_cookie_age', this._maxCookieAge);
}
public setReferences(references: IReferences): void {
super.setReferences(references);
this._settingsClient = this._dependencyResolver.getOneRequired<ISettingsClientV1>('settings');
this._sessionsClient = this._dependencyResolver.getOneRequired<ISessionsClientV1>('sessions');
this._accountsClient = this._dependencyResolver.getOneRequired<IAccountsClientV1>('accounts');
this._passwordsClient = this._dependencyResolver.getOneRequired<IPasswordsClientV1>('passwords');
this._rolesClient = this._dependencyResolver.getOneRequired<IRolesClientV1>('roles');
this._emailSettingsClient = this._dependencyResolver.getOneOptional<IEmailSettingsClientV1>('emailsettings');
this._sitesClient = this._dependencyResolver.getOneRequired<ISitesClientV1>('sites');
this._invitationsClient = this._dependencyResolver.getOneRequired<IInvitationsClientV1>('invitations');
}
public async loadSession(req: any, res: any, next: () => void): Promise<void> {
// parse headers first, and if nothing in headers get cookie
let sessionId = req.headers['x-session-id'];
if (sessionId) {
let session = await this._sessionsClient.getSessionById('facade', sessionId);
if (session == null) {
let err = new UnauthorizedException(
'facade',
'SESSION_NOT_FOUND',
'Session invalid or already expired.'
).withDetails('session_id', sessionId).withStatus(440);
this.sendError(req, res, err);
} else {
// Associate session user with the request
req.user_id = session.user_id;
req.user_name = session.user_name;
req.user = session.user;
req.session_id = session.id;
next();
}
} else {
next();
}
}
public async openSession(req: any, res: any, account: AccountV1, roles: string[]): Promise<void> {
let session: SessionV1;
let sites: SiteV1[];
let passwordInfo: UserPasswordInfoV1;
let settings: ConfigParams;
try {
let siteRoles = roles.filter(r => r.indexOf(':') > 0);
let siteIds = siteRoles.map((r) => {
let pos = r.indexOf(':');
return pos >= 0 ? r.substring(0, pos) : r;
});
if (siteIds.length > 0) {
let filter = FilterParams.fromTuples('ids', siteIds);
let page = await this._sitesClient.getSites(null, filter, null);
sites = page != null ? page.data : [];
} else {
sites = [];
}
passwordInfo = await this._passwordsClient.getPasswordInfo(null, account.id);
settings = await this._settingsClient.getSectionById(null, account.id);
// Open a new user session
let user = <SessionUserV1>{
id: account.id,
name: account.name,
login: account.login,
create_time: account.create_time,
time_zone: account.time_zone,
language: account.language,
theme: account.theme,
roles: roles,
sites: sites.map(s => { return { id: s.id, name: s.name } }),
settings: settings,
change_pwd_time: passwordInfo != null ? passwordInfo.change_time : null,
custom_hdr: account.custom_hdr,
custom_dat: account.custom_dat
};
let address = HttpRequestDetector.detectAddress(req);
let client = HttpRequestDetector.detectBrowser(req);
let platform = HttpRequestDetector.detectPlatform(req);
session = await this._sessionsClient.openSession(
null, account.id, account.name,
address, client, user, null
);
this.sendResult(req, res, session);
} catch (err) {
this.sendError(req, res, err);
}
}
public async signup(req: any, res: any): Promise<void> {
let signupData = req.body;
let account: AccountV1 = null;
let invited: boolean = false;
let roles: string[] = [];
try {
// Validate password first
// Todo: complete implementation after validate password is added
// Create account
let newAccount = <AccountV1>{
name: signupData.name,
login: signupData.login || signupData.email, // Use email by default
language: signupData.language,
theme: signupData.theme,
time_zone: signupData.time_zone
};
account = await this._accountsClient.createAccount(
null, newAccount
)
// Create password for the account
let password = signupData.password;
this._passwordsClient.setPassword(
null, account.id, password
);
// Activate all pending invitations
let email = signupData.email;
let invitations = await this._invitationsClient.activateInvitations(
null, email, account.id);
if (invitations) {
// Calculate user roles from activated invitations
for (let invitation of invitations) {
// Was user invited with the same email?
invited = invited || email == invitation.invitee_email;
if (invitation.site_id) {
invitation.role = invitation.role || 'user';
let role = invitation.site_id + ':' + invitation.role;
roles.push(role);
}
}
}
// Create email settings for the account
let newEmailSettings = <EmailSettingsV1>{
id: account.id,
name: account.name,
email: email,
language: account.language
};
if (this._emailSettingsClient != null) {
if (invited) {
this._emailSettingsClient.setVerifiedSettings(
null, newEmailSettings
);
} else {
this._emailSettingsClient.setSettings(
null, newEmailSettings
);
}
}
await this.openSession(req, res, account, roles);
}
catch (err) {
this.sendError(req, res, err);
}
}
public async signupValidate(req: any, res: any): Promise<void> {
let login = req.param('login');
if (login) {
let account = await this._accountsClient.getAccountByIdOrLogin(null, login);
if (account != null) {
let err = new BadRequestException(
null, 'LOGIN_ALREADY_USED',
'Login ' + login + ' already being used'
).withDetails('login', login);
this.sendError(req, res, err);
} else this.sendEmptyResult(req, res);
}
else {
this.sendEmptyResult(req, req);
}
}
public async signin(req: any, res: any): Promise<void> {
let login = req.param('login');
let password = req.param('password');
let account: AccountV1;
let roles: string[] = [];
try {
// Find user account
account = await this._accountsClient.getAccountByIdOrLogin(null, login);
if (account == null) {
throw new BadRequestException(
null,
'WRONG_LOGIN',
'Account ' + login + ' was not found'
).withDetails('login', login);
}
// Authenticate user
let result = await this._passwordsClient.authenticate(null, account.id, password);
// wrong password error is UNKNOWN when use http client
if (result == false) {
throw new BadRequestException(
null,
'WRONG_PASSWORD',
'Wrong password for account ' + login
).withDetails('login', login);
}
// Retrieve user roles
if (this._rolesClient) {
roles = await this._rolesClient.getRolesById(null, account.id);
}
await this.openSession(req, res, account, roles);
} catch (err) {
this.sendError(req, res, err);
}
}
public async signout(req: any, res: any): Promise<void> {
if (req.session_id) {
try {
await this._sessionsClient.closeSession(null, req.session_id);
this.sendEmptyResult(req, res)
} catch (err) {
this.sendError(req, res, err);
}
} else {
this.sendEmptyResult(req, res)
}
}
public async getSessions(req: any, res: any): Promise<void> {
let filter = this.getFilterParams(req);
let paging = this.getPagingParams(req);
let sessions = await this._sessionsClient.getSessions(
null, filter, paging
);
this.sendResult(req, res, sessions);
}
public async restoreSession(req: any, res: any): Promise<void> {
let sessionId = req.param('session_id');
let session = await this._sessionsClient.getSessionById(null, sessionId);
// If session closed then return null
if (session && !session.active)
session = null;
if (session)
this.sendResult(req, res, session);
else
this.sendEmptyResult(req, res);
}
public async getUserSessions(req: any, res: any): Promise<void> {
let filter = this.getFilterParams(req);
let paging = this.getPagingParams(req);
let userId = req.params.user_id || req.params.account_id;
filter.setAsObject('user_id', userId);
let result = await this._sessionsClient.getSessions(
null, filter, paging
);
this.sendResult(req, res, result);
}
public async getCurrentSession(req: any, res: any): Promise<void> {
// parse headers first, and if nothing in headers get cookie
let sessionId = req.headers['x-session-id'];
let result = await this._sessionsClient.getSessionById(null, sessionId);
this.sendResult(req, res, result);
}
public async closeSession(req: any, res: any): Promise<void> {
let sessionId = req.params.session_id || req.param('session_id');
let result = await this._sessionsClient.closeSession(null, sessionId);
this.sendResult(req, res, result);
}
}
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;
}
}
}
/operations/version1/SessionOperationsV1.go
package operations1
import (
"context"
"encoding/json"
"net/http"
"sync"
"time"
accclients1 "github.com/pip-services-users/pip-clients-accounts-go/version1"
passclients1 "github.com/pip-services-users/pip-clients-passwords-go/version1"
roleclients1 "github.com/pip-services-users/pip-clients-roles-go/version1"
sessclients1 "github.com/pip-services-users/pip-clients-sessions-go/version1"
cconf "github.com/pip-services3-gox/pip-services3-commons-gox/config"
cdata "github.com/pip-services3-gox/pip-services3-commons-gox/data"
cerr "github.com/pip-services3-gox/pip-services3-commons-gox/errors"
cref "github.com/pip-services3-gox/pip-services3-commons-gox/refer"
rpcservices "github.com/pip-services3-gox/pip-services3-rpc-gox/services"
)
type SessionsOperationsV1 struct {
*rpcservices.RestOperations
defaultConfig *cconf.ConfigParams
cookie string
cookieEnabled bool
maxCookieAge int64
accountsClient accclients1.IAccountsClientV1
sessionsClient sessclients1.ISessionsClientV1
passwordsClient passclients1.IPasswordsClientV1
rolesClient roleclients1.IRolesClientV1
}
func NewSessionsOperationsV1() *SessionsOperationsV1 {
c := SessionsOperationsV1{
RestOperations: rpcservices.NewRestOperations(),
}
c.defaultConfig = cconf.NewConfigParamsFromTuples(
"options.cookie_enabled", true,
"options.cookie", "x-session-id",
"options.max_cookie_age", 365*24*60*60*1000,
)
c.cookie = "x-session-id"
c.cookieEnabled = true
c.maxCookieAge = 365 * 24 * 60 * 60 * 1000
c.DependencyResolver.Put("accounts", cref.NewDescriptor("pip-services-accounts", "client", "*", "*", "1.0"))
c.DependencyResolver.Put("passwords", cref.NewDescriptor("pip-services-passwords", "client", "*", "*", "1.0"))
c.DependencyResolver.Put("roles", cref.NewDescriptor("pip-services-roles", "client", "*", "*", "1.0"))
c.DependencyResolver.Put("sessions", cref.NewDescriptor("pip-services-sessions", "client", "*", "*", "1.0"))
return &c
}
func (c *SessionsOperationsV1) Configure(config *cconf.ConfigParams) {
config = config.SetDefaults(c.defaultConfig)
c.DependencyResolver.Configure(config)
c.cookieEnabled = config.GetAsBooleanWithDefault("options.cookie_enabled", c.cookieEnabled)
c.cookie = config.GetAsStringWithDefault("options.cookie", c.cookie)
c.maxCookieAge = config.GetAsLongWithDefault("options.max_cookie_age", c.maxCookieAge)
}
func (c *SessionsOperationsV1) SetReferences(references cref.IReferences) {
c.RestOperations.SetReferences(references)
dependency, _ := c.DependencyResolver.GetOneRequired("sessions")
sesionsClient, ok1 := dependency.(sessclients1.ISessionsClientV1)
if !ok1 {
panic("SessionOperationsV1: Cant't resolv dependency 'client' to ISessionsClientV1")
}
c.sessionsClient = sesionsClient
dependency, _ = c.DependencyResolver.GetOneRequired("accounts")
acountClient, ok2 := dependency.(accclients1.IAccountsClientV1)
if !ok2 {
panic("SessionOperationsV1: Cant't resolv dependency 'client' to IAccountsClientV1")
}
c.accountsClient = acountClient
dependency, _ = c.DependencyResolver.GetOneRequired("passwords")
passClient, ok3 := dependency.(passclients1.IPasswordsClientV1)
if !ok3 {
panic("SessionOperationsV1: Cant't resolv dependency 'client' to IPasswordsClientV1")
}
c.passwordsClient = passClient
dependency, _ = c.DependencyResolver.GetOneRequired("roles")
rolesClient, ok4 := dependency.(roleclients1.IRolesClientV1)
if !ok4 {
panic("SessionOperationsV1: Cant't resolv dependency 'client' to IRolesClientV1")
}
c.rolesClient = rolesClient
}
func (c *SessionsOperationsV1) LoadSession(res http.ResponseWriter, req *http.Request, next http.HandlerFunc) {
sessionId := req.Header.Get("x-session-id")
if sessionId != "" {
session, err := c.sessionsClient.GetSessionById("facade", sessionId)
if session == nil && err == nil {
err = cerr.NewUnauthorizedError(
"facade",
"SESSION_NOT_FOUND",
"Session invalid or already expired.",
).WithDetails("session_id", sessionId).WithStatus(440)
}
if err == nil {
// Associate session user with the request
buf, _ := json.Marshal(session.User)
user := make(map[string]interface{}, 0)
json.Unmarshal(buf, &user)
req = req.WithContext(context.WithValue(req.Context(), "user_id", session.UserId))
req = req.WithContext(context.WithValue(req.Context(), "user_name", session.UserName))
req = req.WithContext(context.WithValue(req.Context(), "user", *cdata.NewAnyValueMapFromValue(user)))
req = req.WithContext(context.WithValue(req.Context(), "session_id", session.Id))
next.ServeHTTP(res, req)
} else {
c.SendError(res, req, err)
}
} else {
next.ServeHTTP(res, req)
}
}
func (c *SessionsOperationsV1) OpenSession(res http.ResponseWriter, req *http.Request, account *accclients1.AccountV1, roles []string) {
var session *sessclients1.SessionV1
var settings cconf.ConfigParams
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
passwordInfo, err := c.passwordsClient.GetPasswordInfo("", account.Id)
if err != nil {
c.SendError(res, req, err)
return
}
var changePwdTime time.Time
if passwordInfo != nil {
changePwdTime = passwordInfo.ChangeTime
}
var user = SessionUserV1{
Id: account.Id,
Name: account.Name,
Login: account.Login,
CreateTime: account.CreateTime,
TimeZone: account.TimeZone,
Language: account.Language,
Theme: account.Theme,
Roles: roles,
Settings: settings,
ChangePwdTime: changePwdTime,
CustomHdr: account.CustomHdr,
CustomDat: account.CustomDat,
}
address := rpcservices.HttpRequestDetector.DetectAddress(req)
client := rpcservices.HttpRequestDetector.DetectBrowser(req)
session, err = c.sessionsClient.OpenSession("", account.Id, account.Name, address, client, user, nil)
if err != nil {
c.SendError(res, req, err)
return
}
//res.json(session)
c.SendResult(res, req, session, nil)
}()
wg.Wait()
}
func (c *SessionsOperationsV1) Signup(res http.ResponseWriter, req *http.Request) {
signupData := make(map[string]interface{})
c.DecodeBody(req, &signupData)
r, ok := signupData["roles"].([]string)
roles := make([]string, 0)
if ok {
roles = append(roles, r...)
}
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
// Use email by default
login, ok := signupData["login"].(string)
if !ok {
login, _ = signupData["email"].(string)
}
// Create account
newAccount := accclients1.AccountV1{}
newAccount.Name, _ = signupData["name"].(string)
newAccount.Login = login
newAccount.Language, _ = signupData["language"].(string)
newAccount.Theme, _ = signupData["theme"].(string)
newAccount.TimeZone, _ = signupData["time_zone"].(string)
account, err := c.accountsClient.CreateAccount("", &newAccount)
if err != nil {
c.SendError(res, req, err)
return
}
// Create password for the account
password, _ := signupData["password"].(string)
err = c.passwordsClient.SetPassword(
"", account.Id, password)
if err != nil {
c.SendError(res, req, err)
return
}
// Create roles for the account
if len(roles) > 0 {
c.rolesClient.GrantRoles(
"", account.Id, roles)
}
c.OpenSession(res, req, account, roles)
}()
wg.Wait()
}
func (c *SessionsOperationsV1) Signin(res http.ResponseWriter, req *http.Request) {
login := c.GetParam(req, "login")
password := c.GetParam(req, "password")
if login == "" && password == "" {
params := make(map[string]string, 0)
c.DecodeBody(req, ¶ms)
login = params["login"]
password = params["password"]
}
roles := make([]string, 0)
wg := sync.WaitGroup{}
wg.Add(1)
go func() {
defer wg.Done()
// Find user account
account, err := c.accountsClient.GetAccountByIdOrLogin("", login)
if err != nil {
c.SendError(res, req, err)
return
}
if err == nil && account == nil {
err = cerr.NewBadRequestError(
"",
"WRONG_LOGIN",
"Account "+login+" was not found",
).WithDetails("login", login)
if err != nil {
c.SendError(res, req, err)
return
}
}
// Authenticate user
result, err := c.passwordsClient.Authenticate("", account.Id, password)
// wrong password error is UNKNOWN when use http client
if (err == nil && result == false) || (err != nil && err.(*cerr.ApplicationError).Cause == "Invalid password") {
err = cerr.NewBadRequestError(
"",
"WRONG_PASSWORD",
"Wrong password for account "+login,
).WithDetails("login", login)
if err != nil {
c.SendError(res, req, err)
return
}
}
// Retrieve user roles
if c.rolesClient != nil {
roles, err = c.rolesClient.GetRolesById("", account.Id)
if err != nil {
c.SendError(res, req, err)
return
}
} else {
roles = make([]string, 0)
}
c.OpenSession(res, req, account, roles)
}()
wg.Wait()
}
func (c *SessionsOperationsV1) Signout(res http.ResponseWriter, req *http.Request) {
sessionId, ok := req.Context().Value("session_id").(string)
if ok {
_, err := c.sessionsClient.CloseSession("", sessionId)
if err != nil {
c.SendError(res, req, err)
} else {
// res.json(204);
c.SendEmptyResult(res, req, nil)
}
} else {
//res.json(204);
c.SendEmptyResult(res, req, nil)
}
}
pip_facades_sample_python/operations/version1/SessionOperationsV1.py
# -*- coding: utf-8 -*-
from typing import List
import bottle
from pip_services3_commons.config import ConfigParams
from pip_services3_commons.convert import JsonConverter, TypeCode
from pip_services3_commons.data import FilterParams
from pip_services3_commons.errors import UnauthorizedException, BadRequestException
from pip_services3_commons.refer import IReferences, Descriptor
from pip_services3_rpc.services import RestOperations
from pip_services3_rpc.services.HttpRequestDetector import HttpRequestDetector
from pip_facades_sample_python.clients.version1.AccountV1 import AccountV1
from pip_facades_sample_python.clients.version1.EmailSettingsV1 import EmailSettingsV1
from pip_facades_sample_python.clients.version1.IAccountsClientV1 import IAccountsClientV1
from pip_facades_sample_python.clients.version1.IEmailSettingsClientV1 import IEmailSettingsClientV1
from pip_facades_sample_python.clients.version1.IInvitationsClientV1 import IInvitationsClientV1
from pip_facades_sample_python.clients.version1.IPasswordsClientV1 import IPasswordsClientV1
from pip_facades_sample_python.clients.version1.IRolesClientV1 import IRolesClientV1
from pip_facades_sample_python.clients.version1.ISessionsClientV1 import ISessionsClientV1
from pip_facades_sample_python.clients.version1.ISettingsClientV1 import ISettingsClientV1
from pip_facades_sample_python.clients.version1.ISitesClientV1 import ISitesClientV1
from pip_facades_sample_python.clients.version1.SessionV1 import SessionV1
from pip_facades_sample_python.clients.version1.SiteV1 import SiteV1
from pip_facades_sample_python.clients.version1.UserPasswordInfoV1 import UserPasswordInfoV1
from pip_facades_sample_python.operations.version1.SessionUserV1 import SessionUserV1
class SessionsOperationsV1(RestOperations):
__default_config = ConfigParams.from_tuples(
'options.cookie_enabled', True,
'options.cookie', 'x-session-id',
'options.max_cookie_age', 365 * 24 * 60 * 60 * 1000
)
def __init__(self):
super().__init__()
self.__cookie: str = 'x-session-id'
self.__cookie_enabled: bool = True
self.__max_cookie_age: float = 365 * 24 * 60 * 60 * 1000
self.__settings_client: ISettingsClientV1 = None
self.__accounts_client: IAccountsClientV1 = None
self.__sessions_client: ISessionsClientV1 = None
self.__passwords_client: IPasswordsClientV1 = None
self.__roles_client: IRolesClientV1 = None
self.__email_settings_client: IEmailSettingsClientV1 = None
self.__sites_client: ISitesClientV1 = None
self.__invitations_client: IInvitationsClientV1 = None
self._dependency_resolver.put('settings', Descriptor('pip-services-settings', 'client', '*', '*', '1.0'))
self._dependency_resolver.put('accounts', Descriptor('pip-services-accounts', 'client', '*', '*', '1.0'))
self._dependency_resolver.put('passwords', Descriptor('pip-services-passwords', 'client', '*', '*', '1.0'))
self._dependency_resolver.put('roles', Descriptor('pip-services-roles', 'client', '*', '*', '1.0'))
self._dependency_resolver.put('emailsettings',
Descriptor('pip-services-emailsettings', 'client', '*', '*', '1.0'))
self._dependency_resolver.put('sessions', Descriptor('pip-services-sessions', 'client', '*', '*', '1.0'))
self._dependency_resolver.put('sites', Descriptor('pip-services-sites', 'client', '*', '*', '1.0'))
self._dependency_resolver.put('invitations', Descriptor('pip-services-invitations', 'client', '*', '*', '1.0'))
def configure(self, config: ConfigParams):
config = config.set_defaults(SessionsOperationsV1.__default_config)
self._dependency_resolver.configure(config)
self.__cookie_enabled = config.get_as_boolean_with_default('options.cookie_enabled', self.__cookie_enabled)
self.__cookie = config.get_as_string_with_default('options.cookie', self.__cookie)
self.__max_cookie_age = config.get_as_long_with_default('options.max_cookie_age', self.__max_cookie_age)
def set_references(self, references: IReferences):
super().set_references(references)
self.__settings_client = self._dependency_resolver.get_one_required('settings')
self.__sessions_client = self._dependency_resolver.get_one_required('sessions')
self.__accounts_client = self._dependency_resolver.get_one_required('accounts')
self.__passwords_client = self._dependency_resolver.get_one_required('passwords')
self.__roles_client = self._dependency_resolver.get_one_required('roles')
self.__email_settings_client = self._dependency_resolver.get_one_required('emailsettings')
self.__sites_client = self._dependency_resolver.get_one_required('sites')
self.__invitations_client = self._dependency_resolver.get_one_required('invitations')
def load_session(self):
# parse headers first, and if nothing in headers get cookie
session_id = bottle.request.headers.get('x-session-id')
session, err = None, None
if session_id:
try:
session = self.__sessions_client.get_session_by_id('facade', session_id)
if session is None:
raise UnauthorizedException(
'facade',
'SESSION_NOT_FOUND',
'Session invalid or already expired.'
).with_details('session_id', session_id).with_status(440)
except Exception as err:
return self._send_error(err)
if err is None and session:
# Associate session user with the request
bottle.request.user_id = session.user_id
bottle.request.user_name = session.user_name
bottle.request.user = session.user
bottle.request.session_id = session.id
else:
return self._send_error(err)
def open_session(self, account: AccountV1, roles: List[str], ):
try:
session: SessionV1 = None
sites: List[SiteV1] = None
passwordInfo: UserPasswordInfoV1 = None
settings: ConfigParams = None
# Retrieve sites for user
site_roles = [] if not roles else list(filter(lambda x: x.find(':') > 0, roles))
site_ids = [] if not site_roles else list(map(lambda x: x[0:x.find(':')] if x.find(':') >= 0 else x, site_roles))
if len(site_ids) > 0:
filter_params = FilterParams.from_tuples('ids', site_ids)
page = self.__sites_client.get_sites(None, filter_params, None)
sites = [] if page is None else page.data
else:
sites = []
password_info = self.__passwords_client.get_password_info(None, account.id)
settings = self.__settings_client.get_section_by_id(None, account.id)
# Open a new user session
user = SessionUserV1(
id=account.id,
name=account.name,
login=account.login,
create_time=account.create_time,
time_zone=account.time_zone,
language=account.language,
theme=account.theme,
roles=roles,
sites=list(map(lambda x: {'id': x.id, 'name': x.name}, sites)),
settings=settings,
change_pwd_time=None if password_info is None else password_info.change_time,
custom_hdr=account.custom_hdr,
custom_dat=account.custom_dat
)
address = HttpRequestDetector.detect_address(bottle.request)
client = HttpRequestDetector.detect_browser(bottle.request)
platform = HttpRequestDetector.detect_platform(bottle.request)
session = self.__sessions_client.open_session(None, account.id, account.name, address, client, user, None)
return JsonConverter.to_json(session)
except Exception as err:
return self._send_error(err)
def signup(self):
try:
signup_data = bottle.request.json if isinstance(bottle.request.json, dict) else JsonConverter.from_json(
TypeCode.Map, bottle.request.json)
account: AccountV1 = None
invited: bool = False
roles: List[str] = []
# Validate password first
# Todo: complete implementation after validate password is added
# Create account
new_account = AccountV1(
name=signup_data.get('name'),
login=signup_data.get('login') or signup_data.get('email'),
language=signup_data.get('language'),
theme=signup_data.get('theme'),
time_zone=signup_data.get('time_zone')
)
account = self.__accounts_client.create_account(None, new_account)
# Create password for the account
password = signup_data.get('password')
self.__passwords_client.set_password(None, account.id, password)
# Activate all pending invitations
email = signup_data.get('email')
invitations = self.__invitations_client.activate_invitations(None, email, account.id)
if invitations:
# Calculate user roles from activated invitations
for invitation in invitations:
# Was user invited with the same email?
invited = invited or email == invitation.invitee_email
if invitation.site_id:
invitation.role = invitation.role or 'user'
role = invitation.site_id + ':' + invitation.role
roles.append(role)
# Create email settings for the account
new_email_settings = EmailSettingsV1(
id=account.id,
name=account.name,
email=email,
language=account.language
)
if self.__email_settings_client is not None:
if invited:
self.__email_settings_client.set_verified_settings(None, new_email_settings)
else:
self.__email_settings_client.set_settings(None, new_email_settings)
return self.open_session(account, roles)
except Exception as err:
return self._send_error(err)
def signup_validate(self):
login = dict(bottle.request.query.decode()).get('login')
if login:
try:
account = self.__accounts_client.get_account_by_id_or_login(None, login)
if account:
raise BadRequestException(
None, 'LOGIN_ALREADY_USED',
'Login ' + login + ' already being used'
).with_details('login', login)
return bottle.HTTPResponse(status=204)
except Exception as err:
return self._send_error(err)
else:
return bottle.HTTPResponse(status=204)
def signin(self):
json_data = bottle.request.json if isinstance(bottle.request.json, dict) else JsonConverter.from_json(
TypeCode.Map, bottle.request.json)
login = json_data.get('login')
password = json_data.get('password')
account: AccountV1 = None
roles: List[str] = []
# Find user account
try:
account = self.__accounts_client.get_account_by_id_or_login(None, login)
if account is None:
raise BadRequestException(
None,
'WRONG_LOGIN',
'Account ' + login + ' was not found'
).with_details('login', login)
# Authenticate user
result = self.__passwords_client.authenticate(None, account.id, password)
# wrong password error is UNKNOWN when use http client
if result is False:
raise BadRequestException(
None,
'WRONG_PASSWORD',
'Wrong password for account ' + login
).with_details('login', login)
# Retrieve user roles
if self.__roles_client:
roles = self.__roles_client.get_roles_by_id(None, account.id, )
else:
roles = []
return self.open_session(account, roles)
except Exception as err:
return self._send_error(err)
def signout(self):
if hasattr(bottle.request, 'session_id'):
try:
self.__sessions_client.close_session(None, bottle.request.session_id)
return bottle.HTTPResponse(status=204)
except Exception as err:
self._send_error(err)
return bottle.HTTPResponse(status=204)
def get_sessions(self):
filter_params = self._get_filter_params()
paging = self._get_paging_params()
result = self.__sessions_client.get_sessions(None, filter_params, paging)
return self._send_result(result)
def restore_session(self):
try:
session_id = dict(bottle.request.query.decode()).get('session_id')
session = self.__sessions_client.get_session_by_id(None, session_id)
# If session closed then return null
if session and not session.active:
session = None
if session:
return JsonConverter.to_json(session)
else:
return bottle.HTTPResponse(status=204)
except Exception as err:
return self._send_error(err)
def get_user_sessions(self, user_id):
filter_params = self._get_filter_params()
paging = self._get_paging_params()
filter_params.set_as_object('user_id', user_id)
sessions = self.__sessions_client.get_sessions(None, filter_params, paging)
return self._send_result(sessions.to_json())
def get_current_session(self):
# parse headers first, and if nothing in headers get cookie
session_id = bottle.request.headers.get('x-session-id')
session = self.__sessions_client.get_session_by_id(None, session_id)
return self._send_result(session)
def close_session(self, user_id, session_id):
result = self.__sessions_client.close_session(None, session_id)
return self._send_result(result)
This class contains the main operations for managing sessions, which allow us to load existing sessions, create new sessions, close existing sessions, and also authenticate users. This class depends on four microservices - Accounts, Passwords, Roles, and Sessions - each of which is responsible for a certain part of the class’s logic.
The signup and signin methods perform registration of new users and authentication of existing ones, respectively. Upon successful registration or authorization, the handlers of these operations open a new session as the finishing step of these methods.
The signout method closes sessions when users leave the system, or automatically when the session expires.
The load_session interceptor checks a session’s validity, and whether or not it even exists. The interceptor checks the request’s header for a session ID and, if one is found, uses it to retrieve data about the session from the Sessions microservice. If the session is expired or incorrect, an error will be returned. If everything’s all right, information about the user and the session are extracted and attached to the request, and a “green light” is given for further processing.
To perform these operations, our microservice needs to be able to interact with the microservices it depends on. This communication is made simple using standard clients, the links to which are set in the setReferences method.
The authorization mechanism will be responsible for limiting access to resources, depending on the roles a user has been assigned. This part’s implementation will be discussed in Step 5 - Authorization.