/
evortexcp-backend.go
250 lines (207 loc) · 5.72 KB
/
evortexcp-backend.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
package main
import (
"database/sql"
"errors"
"fmt"
"log"
"net"
"golang.org/x/crypto/bcrypt"
"github.com/lheinrichde/evortexcp-backend/internal/app/handlers"
"github.com/lheinrichde/golib/pkg/config"
"github.com/lheinrichde/golib/pkg/crypter"
"github.com/lheinrichde/golib/pkg/db"
"github.com/lheinrichde/golib/pkg/handler"
"github.com/lheinrichde/golib/pkg/module"
"github.com/lheinrichde/golib/pkg/setup"
)
// main function
func main() {
var err error
// config
err = config.LoadConfig("config.json")
if err != nil {
panic(err)
}
// log
if config.Get("app", "logType") == "file" {
err = setup.LogToFile(config.Get("app", "logFile"))
if err != nil {
panic(err)
}
}
// database
err = setupDB()
if err != nil {
panic(err)
}
// server
err = startServer()
if err != nil {
panic(err)
}
registerHandlers()
// started
fmt.Println("evortexcp-backend (c) 2018 Lennart Heinrich")
// modules
err = module.LoadModules(config.Get("app", "modules"))
if err != nil {
panic(err)
}
// keep open
<-make(chan bool)
}
// register known handlers
func registerHandlers() {
handler.Add("getperms", handlers.GetPerms)
handler.Add("getroleid", handlers.GetRoleID)
handler.Add("getrolename", handlers.GetRoleName)
handler.Add("getusers", handlers.GetUsers)
handler.Add("getroles", handlers.GetRoles)
handler.Add("createuser", handlers.CreateUser)
handler.Add("deleteuser", handlers.DeleteUser)
handler.Add("createrole", handlers.CreateRole)
handler.Add("deleterole", handlers.DeleteRole)
handler.Add("checkmodule", handlers.CheckModule)
handler.Add("deinstallmodule", handlers.DeinstallModule)
handler.Add("changepassword", handlers.ChangePassword)
}
// connect to and setup database
func setupDB() error {
var err error
// unused sql query values
var trash string
// connect to databse
db.Connect(config.Get("postgresql", "host"), config.Get("postgresql", "port"), config.Get("postgresql", "ssl"),
config.Get("postgresql", "database"), config.Get("postgresql", "username"), config.Get("postgresql", "password"))
// setup tables
_, err = db.DB.Exec(`-- Setup
-- Roles
CREATE TABLE IF NOT EXISTS roles (roleID SERIAL, roleName VARCHAR(255) UNIQUE,
PRIMARY KEY (roleID));
-- Permissions
CREATE TABLE IF NOT EXISTS permissions (role INT, permission VARCHAR(255),
FOREIGN KEY (role) REFERENCES roles (roleID) ON DELETE CASCADE);
-- Users
CREATE TABLE IF NOT EXISTS users (username VARCHAR(255) UNIQUE, passwordHash VARCHAR(255), role INT,
PRIMARY KEY(username), FOREIGN KEY (role) REFERENCES roles (roleID) ON DELETE CASCADE);`)
if err != nil {
return err
}
// check if a role exists
err = db.DB.QueryRow("SELECT roleID FROM roles;").Scan(&trash)
if err == sql.ErrNoRows {
// create default role
_, err = db.DB.Exec(`INSERT INTO roles (roleID, roleName) VALUES (1, 'admin');
INSERT INTO permissions (role, permission) VALUES (1, '*');`)
if err != nil {
return err
}
} else if err != nil {
// return error
return err
}
// check if a user exists
err = db.DB.QueryRow("SELECT username FROM users;").Scan(&trash)
if err == sql.ErrNoRows {
// generate bcrypt hash
passwordHashSHA3 := crypter.Hash("evortexcp_" + crypter.Hash("admin"))
var passwordHash []byte
passwordHash, err = bcrypt.GenerateFromPassword([]byte(passwordHashSHA3), bcrypt.DefaultCost+1)
if err != nil {
return err
}
// create default user
_, err = db.DB.Exec("INSERT INTO users (username, passwordHash, role) VALUES ('admin', $1, 1);", string(passwordHash))
if err != nil {
return err
}
} else if err != nil {
// return error
return err
}
return nil
}
// start listener
func startServer() error {
var err error
// listen to address
var listener net.Listener
listener, err = net.Listen("tcp", config.Get("server", "address"))
if err != nil {
return err
}
// async
go listen(listener)
return nil
}
// listener for incoming connections
func listen(listener net.Listener) {
var err error
// add default handler
handler.Add("default", handlers.Default)
for {
// accept connection
var conn net.Conn
conn, err = listener.Accept()
if err != nil {
log.Println(err)
}
// handle connection
go handleConnSafe(conn)
}
}
// handle connection and close
func handleConnSafe(conn net.Conn) {
var err error
err = handleConn(conn)
if err != nil {
handler.Write(conn, map[string]interface{}{"error": err.Error()})
}
conn.Close()
}
// handle connection
func handleConn(conn net.Conn) error {
var err error
// define variables
var request map[string]interface{}
request, err = handler.Read(conn)
if err != nil {
return err
}
typ, username, password := handler.GetString(request, "type"), handler.GetString(request, "username"), handler.GetString(request, "password")
// close connection if no type, username and password defined
if typ == "" {
return errors.New("404")
} else if username == "" || password == "" {
return errors.New("403")
}
// verify login
role := verifyLogin(username, password)
response := map[string]interface{}{}
// process verification
if role == nil {
// wrong login
return errors.New("403")
} else if typ == "login" {
// respond with role id
response["roleID"] = *role
handler.Write(conn, response)
return nil
}
// handle with handler
return handler.Get(typ)(conn, request, username)
}
// verify login
func verifyLogin(username, password string) *int {
var err error
// query database for user role
var passwordHash string
var role int
err = db.DB.QueryRow("SELECT passwordHash, role FROM users WHERE username = $1", username).Scan(&passwordHash, &role)
// not exists or wrong login data
if err == sql.ErrNoRows || bcrypt.CompareHashAndPassword([]byte(passwordHash), []byte(password)) != nil {
return nil
}
// return role
return &role
}