mirror of
https://gitlab.com/comunic/comunicapiv2
synced 2024-11-22 13:29:22 +00:00
Sign in user
This commit is contained in:
parent
89e612900c
commit
293752b0f4
@ -1,4 +1,5 @@
|
|||||||
import { RequestHandler } from "../entities/RequestHandler";
|
import { RequestHandler } from "../entities/RequestHandler";
|
||||||
|
import { AccountHelper } from "../helpers/AccountHelper";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Account controller
|
* Account controller
|
||||||
@ -13,9 +14,31 @@ export class AccountController {
|
|||||||
*
|
*
|
||||||
* @param handler
|
* @param handler
|
||||||
*/
|
*/
|
||||||
public static LoginUser(handler: RequestHandler) {
|
public static async LoginUser(handler: RequestHandler) {
|
||||||
|
|
||||||
handler.success("Successful operation.");
|
// Get post data
|
||||||
|
const email = handler.postEmail("userMail");
|
||||||
|
const password = handler.postString("userPassword");
|
||||||
|
|
||||||
|
// TODO : add limits
|
||||||
|
|
||||||
|
// Authenticate user
|
||||||
|
const tokens = await AccountHelper.LoginUser(email, password, handler.getClientInfo());
|
||||||
|
|
||||||
|
if(tokens == null) {
|
||||||
|
// TODO : add limits
|
||||||
|
|
||||||
|
handler.error(401, "Invalid e-mail address / password !");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Success
|
||||||
|
handler.send({
|
||||||
|
success: "User signed in!",
|
||||||
|
tokens: {
|
||||||
|
token1: tokens.token1,
|
||||||
|
token2: tokens.token2
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import { Response, Request } from "express";
|
import { Response, Request } from "express";
|
||||||
import { APIHelper } from "../helpers/APIHelper";
|
import { APIHelper } from "../helpers/APIHelper";
|
||||||
import { APIClient } from "./APIClient";
|
import { APIClient } from "./APIClient";
|
||||||
|
import { checkMail } from "../utils/StringUtils";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Response to a request
|
* Response to a request
|
||||||
@ -31,7 +32,7 @@ export class RequestHandler {
|
|||||||
* @param required If set to true (true by default), an error will
|
* @param required If set to true (true by default), an error will
|
||||||
* be thrown if the string is not included in the request
|
* be thrown if the string is not included in the request
|
||||||
*/
|
*/
|
||||||
public getString(name : string, minLength : number = 1, required : boolean = true) : string {
|
public postString(name : string, minLength : number = 1, required : boolean = true) : string {
|
||||||
const param = this.getPostParam(name);
|
const param = this.getPostParam(name);
|
||||||
|
|
||||||
// Check if parameter was not found
|
// Check if parameter was not found
|
||||||
@ -47,6 +48,20 @@ export class RequestHandler {
|
|||||||
return param;
|
return param;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get an email address included in a post request
|
||||||
|
*
|
||||||
|
* @param name The name of the POST filed
|
||||||
|
*/
|
||||||
|
public postEmail(name: string) : string {
|
||||||
|
const email = this.postString(name, 3);
|
||||||
|
|
||||||
|
if(!checkMail(email))
|
||||||
|
this.error(400, email + " is not a valid email address!");
|
||||||
|
|
||||||
|
return email;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate API tokens
|
* Validate API tokens
|
||||||
@ -56,8 +71,8 @@ export class RequestHandler {
|
|||||||
public async checkAPITokens() {
|
public async checkAPITokens() {
|
||||||
|
|
||||||
// Extract API name & token from request
|
// Extract API name & token from request
|
||||||
const apiName = this.getString("serviceName");
|
const apiName = this.postString("serviceName");
|
||||||
const apiToken = this.getString("serviceToken");
|
const apiToken = this.postString("serviceToken");
|
||||||
|
|
||||||
// Validate the client
|
// Validate the client
|
||||||
const client = await APIHelper.GetClient(apiName, apiToken);
|
const client = await APIHelper.GetClient(apiName, apiToken);
|
||||||
@ -81,7 +96,18 @@ export class RequestHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Output an error code
|
* Get information about API client
|
||||||
|
*/
|
||||||
|
public getClientInfo() : APIClient {
|
||||||
|
|
||||||
|
if(!this.client)
|
||||||
|
throw Error("Try to get information about client but client has not been authenticated!");
|
||||||
|
|
||||||
|
return this.client;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Output an error code and throws an error
|
||||||
*
|
*
|
||||||
* @param code HTTP Status code
|
* @param code HTTP Status code
|
||||||
* @param message The message to send
|
* @param message The message to send
|
||||||
|
12
src/entities/UserLoginTokens.ts
Normal file
12
src/entities/UserLoginTokens.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
/**
|
||||||
|
* User tokens
|
||||||
|
*
|
||||||
|
* @author Pierre HUBERT
|
||||||
|
*/
|
||||||
|
|
||||||
|
export interface UserLoginTokens {
|
||||||
|
userID : number,
|
||||||
|
clientID : number,
|
||||||
|
token1: string,
|
||||||
|
token2: string,
|
||||||
|
}
|
102
src/helpers/AccountHelper.ts
Normal file
102
src/helpers/AccountHelper.ts
Normal file
@ -0,0 +1,102 @@
|
|||||||
|
import { crypt, sha1, randomStr } from "../utils/CryptUtils";
|
||||||
|
import { APIClient } from "../entities/APIClient";
|
||||||
|
import { UserLoginTokens } from "../entities/UserLoginTokens";
|
||||||
|
import { DatabaseHelper } from "./DatabaseHelper";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Account helper
|
||||||
|
*
|
||||||
|
* @author Pierre HUBERT
|
||||||
|
*/
|
||||||
|
|
||||||
|
const USER_TABLE = "utilisateurs";
|
||||||
|
const USERS_TOKENS_TABLE = "comunic_api_users_tokens";
|
||||||
|
|
||||||
|
export class AccountHelper {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Given email address and password, try to sign in user
|
||||||
|
*
|
||||||
|
* @param email The email of the user
|
||||||
|
* @param password User password
|
||||||
|
* @param client Information about associated client / null if none found
|
||||||
|
*/
|
||||||
|
static async LoginUser(email: string, password: string, client: APIClient) : Promise<UserLoginTokens | null> {
|
||||||
|
|
||||||
|
// Perform a request on the database
|
||||||
|
const row = await DatabaseHelper.QueryRow({
|
||||||
|
table: USER_TABLE,
|
||||||
|
fields: ["ID"],
|
||||||
|
where: {
|
||||||
|
mail: email,
|
||||||
|
password: this.CryptPassword(password)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Check if user was found
|
||||||
|
if(row == null)
|
||||||
|
return null;
|
||||||
|
const userID = row.ID;
|
||||||
|
|
||||||
|
// Check for existing tokens
|
||||||
|
let tokens = await this.GetClientTokens(userID, client);
|
||||||
|
|
||||||
|
if(tokens != null)
|
||||||
|
return tokens;
|
||||||
|
|
||||||
|
const newTokens : UserLoginTokens = {
|
||||||
|
userID: userID,
|
||||||
|
clientID: client.id,
|
||||||
|
token1: randomStr(150),
|
||||||
|
token2: "dummy_data"
|
||||||
|
}
|
||||||
|
|
||||||
|
// Save new tokens
|
||||||
|
await DatabaseHelper.InsertRow(USERS_TOKENS_TABLE, {
|
||||||
|
user_id: newTokens.userID,
|
||||||
|
service_id: newTokens.clientID,
|
||||||
|
token1: newTokens.token1,
|
||||||
|
token2: newTokens.token2
|
||||||
|
});
|
||||||
|
|
||||||
|
return newTokens;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get user client tokens (if it exists)
|
||||||
|
*
|
||||||
|
* @param userID Target user ID
|
||||||
|
* @param client Information about associated client / null if none found
|
||||||
|
*/
|
||||||
|
private static async GetClientTokens(userID: number, client: APIClient): Promise<UserLoginTokens | null> {
|
||||||
|
const row = await DatabaseHelper.QueryRow({
|
||||||
|
table: USERS_TOKENS_TABLE,
|
||||||
|
where: {
|
||||||
|
user_id: userID,
|
||||||
|
service_id: client.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return row == null ? null : this.DBToUserTokens(row);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Crypt a password
|
||||||
|
*
|
||||||
|
* @param pass The password to crypt
|
||||||
|
* @return Encrypted string
|
||||||
|
*/
|
||||||
|
private static CryptPassword(pass: string) : string {
|
||||||
|
return crypt(sha1(pass), sha1(pass));
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private static DBToUserTokens(row : any) : UserLoginTokens {
|
||||||
|
return {
|
||||||
|
userID: row.user_id,
|
||||||
|
clientID: row.service_id,
|
||||||
|
token1: row.token1,
|
||||||
|
token2: row.token2
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
@ -9,6 +9,7 @@ import { conf } from "./ConfigHelper";
|
|||||||
|
|
||||||
export interface QueryInformation {
|
export interface QueryInformation {
|
||||||
table: string,
|
table: string,
|
||||||
|
fields ?: Array<String>,
|
||||||
where ?: Object,
|
where ?: Object,
|
||||||
limit ?: number,
|
limit ?: number,
|
||||||
}
|
}
|
||||||
@ -51,7 +52,12 @@ export class DatabaseHelper {
|
|||||||
*/
|
*/
|
||||||
static async Query(info: QueryInformation) : Promise<Array<any>> {
|
static async Query(info: QueryInformation) : Promise<Array<any>> {
|
||||||
// Prepare SQL request
|
// Prepare SQL request
|
||||||
let request = "SELECT * FROM " + info.table;
|
let request = "SELECT ";
|
||||||
|
|
||||||
|
// Requested fields
|
||||||
|
request += info.fields ? info.fields.join(",") : "*";
|
||||||
|
|
||||||
|
request += " FROM " + info.table;
|
||||||
let args = [];
|
let args = [];
|
||||||
|
|
||||||
// Add where arguments
|
// Add where arguments
|
||||||
@ -61,7 +67,7 @@ export class DatabaseHelper {
|
|||||||
for(const k in info.where) {
|
for(const k in info.where) {
|
||||||
if(!info.where.hasOwnProperty(k))
|
if(!info.where.hasOwnProperty(k))
|
||||||
continue;
|
continue;
|
||||||
const v = info.where[k];
|
const v = info.where[k].toString();
|
||||||
|
|
||||||
request += k;
|
request += k;
|
||||||
request += v.startsWith("%") || v.endsWith("%") ? " LIKE " : " = "
|
request += v.startsWith("%") || v.endsWith("%") ? " LIKE " : " = "
|
||||||
@ -107,4 +113,21 @@ export class DatabaseHelper {
|
|||||||
|
|
||||||
return result[0];
|
return result[0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Insert an new entry into the database
|
||||||
|
*
|
||||||
|
* @param info Information about the entry
|
||||||
|
* @returns The ID of the inserted column (if any)
|
||||||
|
*/
|
||||||
|
static async InsertRow(table : string, values : any) : Promise<number> {
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
this.connection.query("INSERT INTO " + table + " SET ?", values, (err, results, fields) => {
|
||||||
|
if(err)
|
||||||
|
reject(err);
|
||||||
|
|
||||||
|
resolve(results.insertId);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
}
|
}
|
@ -27,4 +27,29 @@ export function sha1(str : string) : string {
|
|||||||
*/
|
*/
|
||||||
export function crypt(str : string, salt: string) : string {
|
export function crypt(str : string, salt: string) : string {
|
||||||
return execSync("/usr/bin/php -r \"echo crypt(\'"+str+"\', \'"+salt+"\');\"").toString();
|
return execSync("/usr/bin/php -r \"echo crypt(\'"+str+"\', \'"+salt+"\');\"").toString();
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random integer
|
||||||
|
*
|
||||||
|
* @param max Maximum value to except
|
||||||
|
*/
|
||||||
|
export function randInt(max: number) : number {
|
||||||
|
return Math.floor(Math.random() * Math.floor(max));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Generate a random string
|
||||||
|
*
|
||||||
|
* @param length The length of the string to generate
|
||||||
|
* @param keyspace Keyspace to use
|
||||||
|
*/
|
||||||
|
export function randomStr(length: number, keyspace : string = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ") : string {
|
||||||
|
let str = "";
|
||||||
|
|
||||||
|
for (let i = 0; i < length; i++) {
|
||||||
|
str += keyspace[randInt(keyspace.length)];
|
||||||
|
}
|
||||||
|
|
||||||
|
return str;
|
||||||
}
|
}
|
15
src/utils/StringUtils.ts
Normal file
15
src/utils/StringUtils.ts
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
/**
|
||||||
|
* String utilities
|
||||||
|
*
|
||||||
|
* @author Pierre HUBERT
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check a given email address
|
||||||
|
*
|
||||||
|
* @param {String} emailAddress The email address to check
|
||||||
|
* @return {Boolean} True for a valid email address / false else
|
||||||
|
*/
|
||||||
|
export function checkMail(emailAddress: string): boolean {
|
||||||
|
return (emailAddress.match(/^[a-zA-Z0-9_.]+@[a-zA-Z0-9-.]{1,}[.][a-zA-Z]{2,8}$/) === null ? false : true);
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user