1. inisialisasi backend golang
This commit is contained in:
47
backend/pkg/config/config.go
Normal file
47
backend/pkg/config/config.go
Normal file
@@ -0,0 +1,47 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"log"
|
||||
|
||||
"github.com/fsnotify/fsnotify"
|
||||
"github.com/spf13/viper"
|
||||
)
|
||||
|
||||
type Reader interface {
|
||||
Get(key string) string
|
||||
}
|
||||
|
||||
type viperConfigReader struct {
|
||||
viper *viper.Viper
|
||||
}
|
||||
|
||||
var Data *viperConfigReader
|
||||
|
||||
func (v viperConfigReader) Get(key string) string {
|
||||
return v.viper.GetString(key)
|
||||
}
|
||||
|
||||
func (v viperConfigReader) GetInt(key string) int {
|
||||
return v.viper.GetInt(key)
|
||||
}
|
||||
|
||||
func Load() {
|
||||
v := viper.New()
|
||||
v.AddConfigPath(".")
|
||||
v.SetConfigName("config")
|
||||
v.SetConfigType("yaml")
|
||||
v.AutomaticEnv()
|
||||
|
||||
err := v.ReadInConfig()
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
Data = &viperConfigReader{
|
||||
viper: v,
|
||||
}
|
||||
v.WatchConfig()
|
||||
v.OnConfigChange(func(e fsnotify.Event) {
|
||||
log.Println("config file changed", e.Name)
|
||||
})
|
||||
return
|
||||
}
|
||||
98
backend/pkg/crypt/crypt.go
Normal file
98
backend/pkg/crypt/crypt.go
Normal file
@@ -0,0 +1,98 @@
|
||||
package crypt
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"encoding/pem"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"com.sismedika.com.absensi/pkg/config"
|
||||
"golang.org/x/crypto/bcrypt"
|
||||
)
|
||||
|
||||
// openssl genrsa -out private.pem 1024
|
||||
// openssl rsa -in private.pem -outform PEM -pubout out public.pem
|
||||
func EncryptPassword(password string) (string, error) {
|
||||
pemData, err := ioutil.ReadFile(config.Data.Get("privatekey"))
|
||||
if err != nil {
|
||||
log.Printf("read key file: %s", err)
|
||||
return "", fmt.Errorf(("INTERNAL_SERVER_ERROR"))
|
||||
}
|
||||
block, _ := pem.Decode(pemData)
|
||||
if block == nil {
|
||||
log.Printf("bad key data: %s", "not PEM-encoded")
|
||||
return "", fmt.Errorf(("INTERNAL_SERVER_ERROR"))
|
||||
}
|
||||
if got, want := block.Type, "RSA PRIVATE KEY"; got != want {
|
||||
log.Printf("unknown key type %q, want %q", got, want)
|
||||
return "", fmt.Errorf(("INTERNAL_SERVER_ERROR"))
|
||||
}
|
||||
// Decode the RSA private key
|
||||
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
log.Printf("bad private key: %s", err)
|
||||
return "", fmt.Errorf(("INTERNAL_SERVER_ERROR"))
|
||||
}
|
||||
|
||||
passEncrypted, err := rsa.EncryptPKCS1v15(rand.Reader, &priv.PublicKey, []byte(password))
|
||||
if err != nil {
|
||||
log.Printf("decrypt: %s\n", err)
|
||||
return "", fmt.Errorf(("DECRYPTION_FAILED"))
|
||||
}
|
||||
|
||||
return base64.StdEncoding.EncodeToString(passEncrypted), nil
|
||||
}
|
||||
|
||||
func DecryptPassword(passEncoded string) ([]byte, error) {
|
||||
/// decrypt password
|
||||
/// openssl genrsa -traditional -out private.pem 1024
|
||||
/// openssl rsa -in private.pem -outform PEM -pubout -out public.pem
|
||||
pemData, err := ioutil.ReadFile(config.Data.Get("privatekey"))
|
||||
if err != nil {
|
||||
log.Printf("read key file: %s", err)
|
||||
return nil, fmt.Errorf(("INTERNAL_SERVER_ERROR"))
|
||||
}
|
||||
block, _ := pem.Decode(pemData)
|
||||
if block == nil {
|
||||
log.Printf("bad key data: %s", "not PEM-encoded")
|
||||
return nil, fmt.Errorf(("INTERNAL_SERVER_ERROR"))
|
||||
}
|
||||
if got, want := block.Type, "RSA PRIVATE KEY"; got != want {
|
||||
log.Printf("unknown key type %q, want %q", got, want)
|
||||
return nil, fmt.Errorf(("INTERNAL_SERVER_ERROR"))
|
||||
}
|
||||
// Decode the RSA private key
|
||||
priv, err := x509.ParsePKCS1PrivateKey(block.Bytes)
|
||||
if err != nil {
|
||||
log.Printf("bad private key: %s", err)
|
||||
return nil, fmt.Errorf(("INTERNAL_SERVER_ERROR"))
|
||||
}
|
||||
|
||||
var passDecrypted []byte
|
||||
var passDecoded []byte
|
||||
passDecoded, err = base64.StdEncoding.DecodeString(passEncoded)
|
||||
if err != nil {
|
||||
log.Printf("base64 decode: %s\n", err)
|
||||
return nil, fmt.Errorf(("BASE64_DECODE_FAILED"))
|
||||
}
|
||||
|
||||
passDecrypted, err = rsa.DecryptPKCS1v15(rand.Reader, priv, []byte(passDecoded))
|
||||
if err != nil {
|
||||
log.Printf("decrypt: %s\n", err)
|
||||
return nil, fmt.Errorf(("DECRYPTION_FAILED"))
|
||||
}
|
||||
|
||||
return passDecrypted, nil
|
||||
}
|
||||
|
||||
func CheckPasswordHash(hash, password string) bool {
|
||||
start := time.Now()
|
||||
err := bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
log.Printf("CompareHashAndPassword execution took %s", time.Since(start))
|
||||
return err == nil
|
||||
}
|
||||
75
backend/pkg/database/database.go
Normal file
75
backend/pkg/database/database.go
Normal file
@@ -0,0 +1,75 @@
|
||||
package database
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"log"
|
||||
"strings"
|
||||
|
||||
"com.sismedika.com.absensi/pkg/config"
|
||||
_ "github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
var Handle *sql.DB
|
||||
|
||||
func InitDB() {
|
||||
dsn := config.Data.Get("DBuser") + ":" + config.Data.Get("DBpass") + "@tcp(" + config.Data.Get("DBhost") + ":" + config.Data.Get("DBport") + ")/" + config.Data.Get("DBname")
|
||||
handle, err := sql.Open("mysql", dsn)
|
||||
if err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
|
||||
if err = handle.Ping(); err != nil {
|
||||
log.Panic(err)
|
||||
}
|
||||
Handle = handle
|
||||
}
|
||||
|
||||
func LogSQL(query string, args ...interface{}) {
|
||||
var buffer bytes.Buffer
|
||||
nArgs := len(args)
|
||||
// Break the string by question marks, iterate over its parts and for each
|
||||
// question mark - append an argument and format the argument according to
|
||||
// it's type, taking into consideration NULL values and quoting strings.
|
||||
for i, part := range strings.Split(query, "?") {
|
||||
buffer.WriteString(part)
|
||||
if i < nArgs {
|
||||
switch a := args[i].(type) {
|
||||
case int:
|
||||
buffer.WriteString(fmt.Sprintf("%d", a))
|
||||
case int64:
|
||||
buffer.WriteString(fmt.Sprintf("%d", a))
|
||||
case bool:
|
||||
buffer.WriteString(fmt.Sprintf("%t", a))
|
||||
case sql.NullBool:
|
||||
if a.Valid {
|
||||
buffer.WriteString(fmt.Sprintf("%t", a.Bool))
|
||||
} else {
|
||||
buffer.WriteString("NULL")
|
||||
}
|
||||
case sql.NullInt64:
|
||||
if a.Valid {
|
||||
buffer.WriteString(fmt.Sprintf("%d", a.Int64))
|
||||
} else {
|
||||
buffer.WriteString("NULL")
|
||||
}
|
||||
case sql.NullString:
|
||||
if a.Valid {
|
||||
buffer.WriteString(fmt.Sprintf("%q", a.String))
|
||||
} else {
|
||||
buffer.WriteString("NULL")
|
||||
}
|
||||
case sql.NullFloat64:
|
||||
if a.Valid {
|
||||
buffer.WriteString(fmt.Sprintf("%f", a.Float64))
|
||||
} else {
|
||||
buffer.WriteString("NULL")
|
||||
}
|
||||
default:
|
||||
buffer.WriteString(fmt.Sprintf("%q", a))
|
||||
}
|
||||
}
|
||||
}
|
||||
log.Print(buffer.String())
|
||||
}
|
||||
78
backend/pkg/jwt/jwt.go
Normal file
78
backend/pkg/jwt/jwt.go
Normal file
@@ -0,0 +1,78 @@
|
||||
package jwt
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"time"
|
||||
|
||||
"com.sismedika.com.absensi/pkg/config"
|
||||
"github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
// var conf = config.Data
|
||||
|
||||
// GenerateToken generates a jwt token and assign a username to it's claims and return it
|
||||
func GenerateToken(user_id int, username string, staff_id int) (string, int64, error) {
|
||||
token := jwt.New(jwt.SigningMethodHS256)
|
||||
/* Create a map to store our claims */
|
||||
claims := token.Claims.(jwt.MapClaims)
|
||||
/* Set token claims */
|
||||
expired := time.Now().Add(time.Minute * time.Duration(config.Data.GetInt("tokenExpiration"))).Unix()
|
||||
claims["user_id"] = user_id
|
||||
claims["user_name"] = username
|
||||
claims["staff_id"] = staff_id
|
||||
claims["exp"] = expired
|
||||
tokenString, err := token.SignedString([]byte(config.Data.Get("secretkey")))
|
||||
if err != nil {
|
||||
return "", 0, fmt.Errorf("GENERATE_TOKEN_FAILED")
|
||||
}
|
||||
return tokenString, expired, nil
|
||||
}
|
||||
|
||||
// ParseToken parses a jwt token and returns the username in it's claims
|
||||
func ParseToken(tokenStr string) (int, string, int, error) {
|
||||
claims := jwt.MapClaims{}
|
||||
|
||||
token, err := jwt.ParseWithClaims(tokenStr, claims, func(token *jwt.Token) (interface{}, error) {
|
||||
return []byte(config.Data.Get("secretkey")), nil
|
||||
})
|
||||
if err != nil {
|
||||
v, _ := err.(*jwt.ValidationError)
|
||||
|
||||
if v.Errors == jwt.ValidationErrorExpired {
|
||||
log.Printf("parse token 1: %v\n", err)
|
||||
return 0, "", 0, fmt.Errorf("TOKEN_EXPIRED")
|
||||
}
|
||||
|
||||
log.Printf("parse token 2: %v\n", err)
|
||||
return 0, "", 0, fmt.Errorf("TOKEN_INVALID")
|
||||
}
|
||||
|
||||
if !token.Valid {
|
||||
log.Printf("token invalid: %v\n", err)
|
||||
return 0, "", 0, fmt.Errorf(("TOKEN_INVALID"))
|
||||
}
|
||||
|
||||
if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid {
|
||||
if !ok {
|
||||
return 0, "", 0, fmt.Errorf(("TOKEN_INVALID"))
|
||||
}
|
||||
// type of user_id is float64: https://stackoverflow.com/questions/70705673/panic-interface-conversion-interface-is-float64-not-int64
|
||||
if claims["user_id"] == nil {
|
||||
return 0, "", 0, fmt.Errorf(("TOKEN_INVALID"))
|
||||
}
|
||||
if claims["user_name"] == nil {
|
||||
return 0, "", 0, fmt.Errorf(("TOKEN_INVALID"))
|
||||
}
|
||||
if claims["staff_id"] == nil {
|
||||
return 0, "", 0, fmt.Errorf(("TOKEN_INVALID"))
|
||||
}
|
||||
user_id := int(claims["user_id"].(float64))
|
||||
user_name := claims["user_name"].(string)
|
||||
staff_id := int(claims["practitioner_id"].(float64))
|
||||
return user_id, user_name, staff_id, nil
|
||||
} else {
|
||||
log.Printf("token claims: %v\n", err)
|
||||
return 0, "", 0, fmt.Errorf(("TOKEN_INVALID"))
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user