Commit ff4b0f2f authored by Lakr Aream's avatar Lakr Aream 👼🏽

完成增加用户

parent 18d8a4a0
......@@ -181,18 +181,18 @@ TABLE `Candiay`.`users` (
"action": "create", # action
"from": "root", # user name
"name": "Lakr233", # any name with a-z A-Z 0-9
"password": "base64editems==", # password could be any
"passwordSHA256": "sha256edpassword", # password could be any, but we only use a SHA256 hash
"role": "admin", # admin, developer, user
"timestamp": 1585910739 # timestamp since 1970
}
```
- Examples:
> http://www.candiay.backend/users/c?action=create&from=root&name=Lakr233&password=base64editems==&role=admin&timestamp=1585910739
> http://www.candiay.backend/users/c?action=create&from=root&name=Lakr233&passwordSHA256=sha256edpassword&role=admin&timestamp=1585910739
- Call Back Example
```
{
"result": "success", # success, fail, invaildSign
"loginToken": "A5BF9EE4", # a token available for 1 day or none
"result": "success", # success, failed, invaildSign
"tips": "exists if failed"
}
```
</details>
......@@ -217,7 +217,7 @@ TABLE `Candiay`.`users` (
- Call Back Example
```
{
"result": "success", # success, fail, invaildSign
"result": "success", # success, failed, invaildSign
}
```
</details>
......@@ -242,7 +242,7 @@ TABLE `Candiay`.`users` (
- Call Back Example
```
{
"result": "success" # success, fail, invaildSign
"result": "success" # success, failed, invaildSign
}
```
</details>
......
......@@ -113,6 +113,11 @@
"@types/mime": "*"
}
},
"@types/uuid": {
"version": "7.0.2",
"resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-7.0.2.tgz",
"integrity": "sha512-8Ly3zIPTnT0/8RCU6Kg/G3uTICf9sRwYOpUzSIM3503tLIKcnJPRuinHhXngJUy2MntrEf6dlpOHXJju90Qh5w=="
},
"@types/yaml": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/@types/yaml/-/yaml-1.2.0.tgz",
......@@ -846,6 +851,11 @@
"resolved": "https://registry.npmjs.org/utils-merge/-/utils-merge-1.0.1.tgz",
"integrity": "sha1-n5VxD1CiZ5R7LMwSR0HBAoQn5xM="
},
"uuid": {
"version": "7.0.3",
"resolved": "https://registry.npmjs.org/uuid/-/uuid-7.0.3.tgz",
"integrity": "sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg=="
},
"vary": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/vary/-/vary-1.1.2.tgz",
......
......@@ -4,10 +4,12 @@
"description": "Candiay Backend",
"main": "dist/main.js",
"dependencies": {
"@types/uuid": "^7.0.2",
"@types/yaml": "^1.2.0",
"deasync": "^0.1.19",
"express": "^4.17.1",
"mysql": "^2.18.1",
"uuid": "^7.0.3",
"yaml": "^1.8.3"
},
"devDependencies": {
......
......@@ -12,12 +12,10 @@ const port = ConfigManager.shared.portToBind
// Register enterpoints
coreApp.get("/", (req, res) => {
if (!StatusManager.shared.configSuccessed) {
res.status(502).send("Application in configuration")
return
}
res.send("Candiay Core Bootstrap Succeed")
})
......@@ -27,7 +25,6 @@ coreApp.get("/users/c", (req, res) => {
return
}
const ret = RequestManager.shared.performSelectorUserManagementRequest(req, res)
// tslint:disable-next-line: no-string-literal
res.status(ret["code"]).send(ret["context"])
})
......
......@@ -10,7 +10,7 @@ export class DataBase {
private port: number
private user: string
private pass: string
private base: string // which database to use
public base: string // which database to use
private connection: any = undefined
......@@ -155,4 +155,5 @@ export class DataBase {
})
}
}
\ No newline at end of file
......@@ -9,17 +9,19 @@ export class RequestManager {
if (ConfigManager.shared.noSign) {
callback(true)
return
}
const clientSignResult = headers["x-candiay-sign"]
if (clientSignResult === "" || clientSignResult === undefined || clientSignResult === null) {
callback(false)
return
}
// tslint:disable-next-line: no-string-literal
const clientTimeStamp:number = Number(params["timestamp"])
if (clientTimeStamp === 0 || clientTimeStamp === undefined || clientTimeStamp === null) {
callback(false)
return
}
let signString = ""
Object.keys(params).sort().forEach((key) => {
......@@ -39,7 +41,6 @@ export class RequestManager {
signString += "&timestamp="
signString += clientTimeStamp
// tslint:disable-next-line: no-string-literal
let fromUserSName = params["from"]
if (fromUserSName === "" || fromUserSName === undefined || fromUserSName === null) {
fromUserSName = "root"
......@@ -48,6 +49,7 @@ export class RequestManager {
UserManager.shared.readTokenFromUser(fromUserSName, (token) => {
if (token === "" || token === null || token === undefined) {
callback(false)
return
} else {
const signed = require('crypto').createHmac('sha1', token.toLowerCase()).update(Buffer.from(signString, 'utf-8')).digest("hex");
if (signed === clientSignResult) {
......@@ -56,11 +58,14 @@ export class RequestManager {
const gap = current - clientTimeStamp
if (gap < 0 || gap > ConfigManager.shared.kikik) {
callback(false)
return
} else {
callback(true)
return
}
} else {
callback(false)
return
}
}
})
......@@ -93,20 +98,56 @@ export class RequestManager {
"context": "Invalid Signature"
}
contextContaienr = resolveObject
} else {
const resolveObject: { [key: string] : any } = {
"code": 888,
"context": "TEST"
}
contextContaienr = resolveObject
return
}
// Doing ~~Steave~~ Jobs
const actionTypeRaw = String(params["action"])
switch (actionTypeRaw.toLowerCase()) {
case "create":
UserManager.shared.readUserType(params["from"], (role) => {
if (role !== "root" && role !== "admin") {
const ro: { [key: string] : any } = {
"code": 401,
"context": "Unauthorized user performing authorized operation"
}
contextContaienr = ro
return
}
const name2create = params["name"]
const passwordHash = params["passwordSHA256"]
const type2create = params["role"]
UserManager.shared.createUser(name2create, passwordHash, type2create, (err) => {
if (err) {
const item = { "result": "failed", "tips": err }
const ro: { [key: string] : any } = {
"code": 418,
"context": JSON.stringify(item)
}
contextContaienr = ro
} else {
const item = { "result": "success" }
const ro: { [key: string] : any } = {
"code": 200,
"context": JSON.stringify(item)
}
contextContaienr = ro
}
})
})
return
default:
const resolveObject: { [key: string] : any } = {
"code": 404,
"context": "User Management Action " + actionTypeRaw + " Not Found"
}
contextContaienr = resolveObject
return
}
})
let currentStamp = (new Date()).getTime()
while (contextContaienr === undefined && currentStamp - beginStamp < 10) {
while (contextContaienr === undefined && currentStamp - beginStamp < 50) {
require('deasync').runLoopOnce()
currentStamp = (new Date()).getTime()
}
......
import { DataBase } from "./mysql"
import { ConfigManager } from "./config"
import { v4 as uuidv4 } from "uuid"
export interface User {
name: string
pass: string
role: string
token: string
guard: number
actived: boolean
......@@ -50,6 +52,7 @@ export class UserManager {
const u: User = {
name: element.name,
pass: element.pass,
role: element.role,
token: element.token,
guard: element.guard,
actived: element.actived,
......@@ -66,27 +69,78 @@ export class UserManager {
}
}
public readTokenFromUser(withName: string, callback: (token: string) => void) {
if (withName === "root") {
callback(ConfigManager.shared.rootTicket)
return
}
private getUserFromList(withName: string, callback: (user: User | undefined) => void) {
this.readUserList((users) => {
// Look for user
// tslint:disable-next-line: prefer-for-of
for (let index = 0; index < this.userListCache.length; index++) {
const element = this.userListCache[index];
if (element.name.toLowerCase() === withName.toLowerCase()) {
// there is a user
callback(element.token)
callback(element)
return
}
}
// User not exists, do nothing
// User not exists
callback(undefined)
})
}
public readTokenFromUser(withName: string, callback: (token: string) => void) {
if (withName === "root") {
callback(ConfigManager.shared.rootTicket)
return
}
this.getUserFromList(withName, (item) => {
if (item !== undefined) {
callback(item.token)
}
})
}
public createUser(name: String, passSHA256: string, role: string) {
public readUserType(withName: string | undefined, callback: (type: string) => void) {
// root, admin, developer, user
if (withName === "root" || withName === null || withName === undefined || withName === "") {
callback("root")
return
}
this.getUserFromList(withName, (item) => {
if (item !== undefined) {
callback(item.role)
}
})
}
public createUser(name: string, passsha: string, role: string, callback: (errTip: string | undefined) => void) {
// tip should be undefined if operation succeed
this.getUserFromList(name, (item) => {
if (item !== undefined) {
callback("User name already taken")
return
}
if (!name.match("^[A-Za-z0-9]+$")) {
callback("User name only allow A-Z a-z 0-9")
return
}
if (passsha.length !== 64 || !passsha.match("^[A-Za-z0-9]+$")) {
callback("User password needs to be passed as SHA256 result")
return
}
if (role === "root" || (role !== "admin" && role !== "developer" && role !== "user")) {
callback("User role should be one in [admin, developer, user]")
return
}
const base = DataBase.shared.base
const uuid = uuidv4()
const timeStamp = (new Date()).getTime() / 1000
const cmd = "INSERT INTO `" + base + "`.`users`(`name`, `pass`, `role`, `token`, `guard`) VALUES ('" + name + "', '" + passsha + "', '" + role + "', '" + uuid + "', " + timeStamp + ")"
DataBase.shared.queryWithCommand(cmd, (err, ret) => {
if (err) {
callback("mysql returns error: " + String(err))
} else {
callback(undefined)
}
})
})
}
}
\ No newline at end of file
......@@ -3,7 +3,9 @@
"extends": ["tslint:recommended"],
"jsRules": {},
"rules": {
"no-console": false
"no-console": false,
"no-string-literal": false,
"prefer-for-of": false
},
"rulesDirectory": []
}
\ No newline at end of file
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment