chore: code tested for cvgen genadmin
This commit is contained in:
parent
107acf491f
commit
a91aa9dac3
157
.gitignore
vendored
Normal file
157
.gitignore
vendored
Normal file
@ -0,0 +1,157 @@
|
||||
build
|
||||
.k
|
||||
OLD
|
||||
tries
|
||||
tmp
|
||||
test
|
||||
# enviroment to load on bin/build
|
||||
.env
|
||||
|
||||
# where souce code is clone with git
|
||||
clone
|
||||
|
||||
# where tools command are found
|
||||
tools
|
||||
|
||||
# where pipeline templates are found
|
||||
templates
|
||||
|
||||
# OSX leaves these everywhere on SMB shares
|
||||
._*
|
||||
|
||||
# OSX trash
|
||||
.DS_Store
|
||||
|
||||
# Eclipse files
|
||||
.classpath
|
||||
.project
|
||||
.settings/**
|
||||
|
||||
# Files generated by JetBrains IDEs, e.g. IntelliJ IDEA
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
# Vscode files
|
||||
.vscode
|
||||
|
||||
# This is where the result of the go build goes
|
||||
/output*/
|
||||
/_output*/
|
||||
/_output
|
||||
|
||||
# Emacs save files
|
||||
*~
|
||||
\#*\#
|
||||
.\#*
|
||||
|
||||
# Vim-related files
|
||||
[._]*.s[a-w][a-z]
|
||||
[._]s[a-w][a-z]
|
||||
*.un~
|
||||
Session.vim
|
||||
.netrwhist
|
||||
|
||||
# cscope-related files
|
||||
cscope.*
|
||||
|
||||
# Go test binaries
|
||||
*.test
|
||||
/hack/.test-cmd-auth
|
||||
|
||||
# JUnit test output from ginkgo e2e tests
|
||||
/junit*.xml
|
||||
|
||||
# Mercurial files
|
||||
**/.hg
|
||||
**/.hg*
|
||||
|
||||
# Vagrant
|
||||
.vagrant
|
||||
network_closure.sh
|
||||
|
||||
# Local cluster env variables
|
||||
/cluster/env.sh
|
||||
|
||||
# Compiled binaries in third_party
|
||||
/third_party/pkg
|
||||
|
||||
# Also ignore etcd installed by hack/install-etcd.sh
|
||||
/third_party/etcd*
|
||||
/default.etcd
|
||||
|
||||
# User cluster configs
|
||||
.kubeconfig
|
||||
|
||||
.tags*
|
||||
|
||||
# Version file for dockerized build
|
||||
.dockerized-kube-version-defs
|
||||
|
||||
# Web UI
|
||||
/www/master/node_modules/
|
||||
/www/master/npm-debug.log
|
||||
/www/master/shared/config/development.json
|
||||
|
||||
# Karma output
|
||||
/www/test_out
|
||||
|
||||
# precommit temporary directories created by ./hack/verify-generated-docs.sh and ./hack/lib/util.sh
|
||||
/_tmp/
|
||||
/doc_tmp/
|
||||
|
||||
# Test artifacts produced by Jenkins jobs
|
||||
/_artifacts/
|
||||
|
||||
# Go dependencies installed on Jenkins
|
||||
/_gopath/
|
||||
|
||||
# Config directories created by gcloud and gsutil on Jenkins
|
||||
/.config/gcloud*/
|
||||
/.gsutil/
|
||||
|
||||
# CoreOS stuff
|
||||
/cluster/libvirt-coreos/coreos_*.img
|
||||
|
||||
# Juju Stuff
|
||||
/cluster/juju/charms/*
|
||||
/cluster/juju/bundles/local.yaml
|
||||
|
||||
# Downloaded Kubernetes binary release
|
||||
/kubernetes/
|
||||
|
||||
# direnv .envrc files
|
||||
.envrc
|
||||
|
||||
# Downloaded kubernetes binary release tar ball
|
||||
kubernetes.tar.gz
|
||||
|
||||
# generated files in any directory
|
||||
# TODO(thockin): uncomment this when we stop committing the generated files.
|
||||
#zz_generated.*
|
||||
zz_generated.openapi.go
|
||||
zz_generated_*_test.go
|
||||
|
||||
# TODO(roycaihw): remove this when we stop committing the generated definition
|
||||
!staging/src/k8s.io/apiextensions-apiserver/pkg/generated/openapi/zz_generated.openapi.go
|
||||
# low-change blueprint in code-generator to notice changes
|
||||
!staging/src/k8s.io/code-generator/_examples/apiserver/openapi/zz_generated.openapi.go
|
||||
# low-change sample-apiserver spec to be compilable when published
|
||||
!staging/src/k8s.io/sample-apiserver/pkg/generated/openapi/zz_generated.openapi.go
|
||||
|
||||
# make-related metadata
|
||||
/.make/
|
||||
|
||||
# Just in time generated data in the source, should never be committed
|
||||
/test/e2e/generated/bindata.go
|
||||
|
||||
# This file used by some vendor repos (e.g. github.com/go-openapi/...) to store secret variables and should not be ignored
|
||||
!\.drone\.sec
|
||||
|
||||
# Godeps workspace
|
||||
/Godeps/_workspace
|
||||
|
||||
/bazel-*
|
||||
*.pyc
|
||||
|
||||
# generated by verify-vendor.sh
|
||||
vendordiff.patch
|
22
Dockerfile
Normal file
22
Dockerfile
Normal file
@ -0,0 +1,22 @@
|
||||
FROM golang:1.17.6 as builder
|
||||
ENV GOOS linux
|
||||
ENV CGO_ENABLED 0
|
||||
# Add a work directory
|
||||
WORKDIR /app
|
||||
# Cache and install dependencies
|
||||
COPY go.mod go.sum ./
|
||||
RUN go mod download
|
||||
# Copy app files
|
||||
COPY . .
|
||||
# Build app
|
||||
RUN go build -o servgen
|
||||
|
||||
FROM alpine:3.15 as production
|
||||
# Add certificates
|
||||
RUN apk add --no-cache ca-certificates
|
||||
# Copy built binary from builder
|
||||
COPY --from=builder servgen .
|
||||
# Expose port
|
||||
#EXPOSE 8080
|
||||
# Exec built binary
|
||||
CMD ./servgen
|
21
LICENSE
Normal file
21
LICENSE
Normal file
@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020-2022 Jesús Pérez
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
62
crypt.go
Normal file
62
crypt.go
Normal file
@ -0,0 +1,62 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"io"
|
||||
)
|
||||
|
||||
func getKey(key []byte) (string,error) {
|
||||
bytes := make([]byte, 32) //generate a random 32 byte key for AES-256
|
||||
if _, err := rand.Read(bytes); err != nil {
|
||||
return "", err
|
||||
}
|
||||
return hex.EncodeToString(bytes),nil
|
||||
}
|
||||
func encrypt(text string, keytext string) (string, error) {
|
||||
key, _ := hex.DecodeString(keytext)
|
||||
plaintext := []byte(text)
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "",err
|
||||
}
|
||||
//GCM - https://en.wikipedia.org/wiki/Galois/Counter_Mode
|
||||
//https://golang.org/pkg/crypto/cipher/#NewGCM
|
||||
aesGCM, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "",err
|
||||
}
|
||||
nonce := make([]byte, aesGCM.NonceSize())
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return "",err
|
||||
}
|
||||
//Encrypt the data using aesGCM.Seal
|
||||
//Since we don't want to save the nonce somewhere else in this case, we add it as a prefix to the encrypted data. The first nonce argument in Seal is the prefix.
|
||||
ciphertext := aesGCM.Seal(nonce, nonce, plaintext, nil)
|
||||
return fmt.Sprintf("%x", ciphertext),nil
|
||||
}
|
||||
|
||||
func decrypt(encodedtext string, keytext string) (string,error) {
|
||||
key, _ := hex.DecodeString(keytext)
|
||||
enc, _ := hex.DecodeString(encodedtext)
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return "",err
|
||||
}
|
||||
aesGCM, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return "",err
|
||||
}
|
||||
nonceSize := aesGCM.NonceSize()
|
||||
//Extract the nonce from the encrypted data
|
||||
nonce, ciphertext := enc[:nonceSize], enc[nonceSize:]
|
||||
//Decrypt the data
|
||||
text, err := aesGCM.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
return "",err
|
||||
}
|
||||
return fmt.Sprintf("%s", text), nil
|
||||
}
|
134
email.go
Normal file
134
email.go
Normal file
@ -0,0 +1,134 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/tls"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cfg "github.com/jesusperez/cfgsrv"
|
||||
// "text/template"
|
||||
"github.com/toorop/go-dkim"
|
||||
mail "github.com/xhit/go-simple-mail/v2"
|
||||
)
|
||||
|
||||
func email(cfg *cfg.Config, tplType string, tplPath string, attachPath string, attachName string, request Mail, bodyData interface{}) error {
|
||||
|
||||
server := mail.NewSMTPClient()
|
||||
|
||||
// SMTP Server
|
||||
server.Host = cfg.MailHost
|
||||
server.Port = cfg.MailPort
|
||||
server.Username = cfg.MailUser
|
||||
server.Password = cfg.MailPswd
|
||||
server.Encryption = mail.EncryptionSTARTTLS
|
||||
|
||||
// Since v2.3.0 you can specified authentication type:
|
||||
// - PLAIN (default)
|
||||
// - LOGIN
|
||||
// - CRAM-MD5
|
||||
// - None
|
||||
server.Authentication = mail.AuthPlain
|
||||
|
||||
// Variable to keep alive connection
|
||||
server.KeepAlive = true
|
||||
|
||||
// Timeout for connect to SMTP Server
|
||||
server.ConnectTimeout = 10 * time.Second
|
||||
|
||||
// Timeout for send the data and wait respond
|
||||
server.SendTimeout = 10 * time.Second
|
||||
|
||||
// Set TLSConfig to provide custom TLS configuration. For example,
|
||||
// to skip TLS verification (useful for testing):
|
||||
server.TLSConfig = &tls.Config{InsecureSkipVerify: true}
|
||||
|
||||
// SMTP client
|
||||
smtpClient,err := server.Connect()
|
||||
if err != nil{
|
||||
return err
|
||||
}
|
||||
|
||||
// New email simple html with inline and CC
|
||||
email := mail.NewMSG()
|
||||
email.SetFrom(request.Sender).AddTo(strings.Join(request.To,","))
|
||||
if len(request.Cc) > 0 {
|
||||
email.AddCc(strings.Join(request.Cc,","))
|
||||
}
|
||||
if len(request.Bcc) > 0 {
|
||||
email.AddBcc(strings.Join(request.Bcc,","))
|
||||
}
|
||||
email.SetSubject(request.Subject)
|
||||
// email.SetListUnsubscribe("<mailto:unsubscribe@example.com?subject=https://example.com/unsubscribe>").
|
||||
|
||||
// email.AddHeader("Date",time.Now().Format(time.RFC1123Z))
|
||||
|
||||
var body bytes.Buffer
|
||||
if len(tplPath) > 0 {
|
||||
t, _ := template.ParseFiles(tplPath)
|
||||
// body.Write([]byte(msg))
|
||||
//body.Write([]byte(fmt.Sprintf("%s \n%s\n\n", subject,mimeHeaders)))
|
||||
t.Execute(&body, bodyData)
|
||||
request.Body = body
|
||||
}
|
||||
contentType := mail.TextPlain
|
||||
|
||||
switch(tplType) {
|
||||
case "text":
|
||||
contentType = mail.TextPlain
|
||||
case "html":
|
||||
contentType = mail.TextHTML
|
||||
}
|
||||
var privateKey []byte
|
||||
// if len(cfg.MailCertPath) > 0 {
|
||||
// var err error
|
||||
// privateKey, err = loadPath(cfg.MailCertPath)
|
||||
// if err != nil {
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
// you can add dkim signature to the email.
|
||||
// to add dkim, you need a private key already created one.
|
||||
if privateKey != nil && len(privateKey) > 0 {
|
||||
options := dkim.NewSigOptions()
|
||||
options.PrivateKey = privateKey
|
||||
options.Domain = cfg.MailCertDom
|
||||
options.Selector = "default"
|
||||
options.SignatureExpireIn = 3600
|
||||
options.Headers = []string{"from", "date", "mime-version", "received", "received"}
|
||||
options.AddSignatureTimestamp = true
|
||||
options.Canonicalization = "relaxed/relaxed"
|
||||
msg := []byte(request.Body.String())
|
||||
errdkim := dkim.Sign(&msg, options)
|
||||
if errdkim != nil {
|
||||
return errdkim
|
||||
}
|
||||
email.SetBody(contentType, string(msg))
|
||||
} else {
|
||||
email.SetBody(contentType, request.Body.String())
|
||||
}
|
||||
if len(attachPath) > 0 {
|
||||
email.Attach(&mail.File{FilePath: attachPath, Name: attachName, Inline: true})
|
||||
}
|
||||
// also you can add body from []byte with SetBodyData, example:
|
||||
// email.SetBodyData(mail.TextHTML, []byte(htmlBody))
|
||||
// or alternative part
|
||||
// email.AddAlternativeData(mail.TextHTML, []byte(htmlBody))
|
||||
|
||||
// always check error after send
|
||||
if email.Error != nil{
|
||||
log.Fatal(email.Error)
|
||||
}
|
||||
// Sending email.
|
||||
// Call Send and pass the client
|
||||
err = email.Send(smtpClient)
|
||||
if err != nil {
|
||||
fmt.Printf("Error send mail: %v",err)
|
||||
return err
|
||||
}
|
||||
fmt.Println("Email Sent!")
|
||||
return nil
|
||||
}
|
133
handlers.go
Normal file
133
handlers.go
Normal file
@ -0,0 +1,133 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
cvdata "github.com/jesusperez/cvdata"
|
||||
)
|
||||
func user_has_role(usr *User, c *gin.Context, rtenv *RouteEnv, role string) (*User,bool) {
|
||||
var user *User
|
||||
var okusr bool
|
||||
if usr == nil {
|
||||
idusr := rtenv.AuthMiddleware.IdentityHandler(c)
|
||||
user, okusr = idusr.(*User)
|
||||
if !okusr || len(user.UserName) == 0 {
|
||||
return nil,false
|
||||
}
|
||||
} else {
|
||||
user = usr
|
||||
}
|
||||
hasRole := false
|
||||
_, okmdl := rtenv.Users.Accounts[user.UserName]
|
||||
if ! okmdl {
|
||||
return nil,false
|
||||
}
|
||||
if rtenv.Cfg.UseAuthz {
|
||||
hasRole,_ = rtenv.Enforcer.HasRoleForUser(user.UserName, role)
|
||||
} else {
|
||||
hasRole = true
|
||||
// TODO fix this if no Cfg.UseAuthz
|
||||
}
|
||||
return user,hasRole
|
||||
}
|
||||
func get_page_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
tkn := ""
|
||||
id := c.Params.ByName(rtenv.Cfg.Routes["page"].Param)
|
||||
role := rtenv.Cfg.AdminRole
|
||||
hasRole := false
|
||||
mdlUsr, okmdl := rtenv.MdlsUsrs[id]
|
||||
if okmdl {
|
||||
if rtenv.Cfg.UseAuthz {
|
||||
hasRole,_ = rtenv.Enforcer.HasRoleForUser(mdlUsr.User, role)
|
||||
}
|
||||
logRoute(c,rtenv,"page",fmt.Sprintf("get /page/%s", id),fmt.Sprintf("get %s (%s %v) %s",mdlUsr.User,role,hasRole,tkn))
|
||||
if rtenv.Cfg.UseJWT {
|
||||
c.HTML(http.StatusOK, "welcome", gin.H{
|
||||
"title": fmt.Sprintf("Main website %s for %s (%v)",id,mdlUsr.User,hasRole),
|
||||
"token": tkn,
|
||||
})
|
||||
} else {
|
||||
c.HTML(http.StatusOK, "welcome", gin.H{
|
||||
"title": fmt.Sprintf("Main website %s for %s (%v)",id,mdlUsr.User,hasRole),
|
||||
})
|
||||
}
|
||||
} else {
|
||||
logRoute(c,rtenv,"page",fmt.Sprintf("get /page/%s", id),fmt.Sprintf("get %s (%s %v) %s",mdlUsr.User,role,hasRole,tkn))
|
||||
c.HTML(http.StatusOK, "welcome", gin.H{
|
||||
"title": fmt.Sprintf("Main website public"),
|
||||
})
|
||||
}
|
||||
}
|
||||
func get_data_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
// fmt.Printf("context: %+v\n", c)
|
||||
target := c.Params.ByName(rtenv.Cfg.Routes["data"].Param)
|
||||
if target == "-" {
|
||||
target = "main"
|
||||
}
|
||||
logRoute(c,rtenv,"data",fmt.Sprintf("get %s",target), fmt.Sprintf("get %s",target))
|
||||
path := fmt.Sprintf("%s/%s.json",rtenv.Cfg.DataDistPath,target)
|
||||
_, err := os.Open(path)
|
||||
if rtenv.Cfg.UseDist && errors.Is(err, os.ErrNotExist) {
|
||||
path = fmt.Sprintf("%s/%s",rtenv.Cfg.DataPath,target)
|
||||
fmt.Printf("YAML path: %+v\n", path)
|
||||
data,error := cvdata.LoadCVData(path, rtenv.Cfg,rtenv.Cfg.UseRepoOnReq)
|
||||
if error != nil {
|
||||
logRoute(c,rtenv,"data",fmt.Sprintf("Error yaml %s",target), fmt.Sprintf("Err %v",error))
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": "Error reading file"})
|
||||
} else {
|
||||
logRoute(c,rtenv,"data",fmt.Sprintf("OK yaml %s",target), fmt.Sprintf("OK %s",target))
|
||||
c.JSON(http.StatusOK, data)
|
||||
}
|
||||
} else {
|
||||
data, error := ioutil.ReadFile(path)
|
||||
if error != nil {
|
||||
logRoute(c,rtenv,"data",fmt.Sprintf("Error json %s",target), fmt.Sprintf("Err %v",error))
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": "Error reading file"})
|
||||
} else {
|
||||
logRoute(c,rtenv,"data",fmt.Sprintf("OK json %s",target), fmt.Sprintf("OK %s",target))
|
||||
c.Data(http.StatusOK, "application/json", data)
|
||||
//c.Data(http.StatusOK, "application/json", []byte(fmt.Sprintf("{\"models\": %s, \"data\": %s}",datamodels,data)))
|
||||
}
|
||||
}
|
||||
}
|
||||
func post_data_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
var cvpost cvdata.CVPostData
|
||||
role := rtenv.Cfg.AdminRole
|
||||
err := c.BindJSON(&cvpost)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save info"})
|
||||
return
|
||||
}
|
||||
if cvpost.U == "" || len(cvpost.Data) == 0 {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"info": "error info"})
|
||||
return
|
||||
}
|
||||
// roles,_ := enforcer.GetRolesForUser(user)
|
||||
hasRole,_ := rtenv.Enforcer.HasRoleForUser(cvpost.U, role)
|
||||
fmt.Printf("%s (%s) %+v\n",cvpost.U, role, hasRole)
|
||||
if !hasRole {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
keys,res := cvpost.Data.Write(rtenv.Cfg)
|
||||
if res != nil {
|
||||
logRoute(c,rtenv,"post_data",fmt.Sprintf("Error post %s: %s",cvpost.U,keys), fmt.Sprintf("error: %+v",res))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save info"})
|
||||
return
|
||||
}
|
||||
if rtenv.Cfg.GenDist {
|
||||
errModel := createRootModels(rtenv.Cfg)
|
||||
if errModel != nil {
|
||||
fmt.Printf("Error createRootModels: %v\n",errModel)
|
||||
}
|
||||
}
|
||||
logRoute(c,rtenv,"post_data",fmt.Sprintf("post %s: %s",cvpost.U,keys), fmt.Sprintf("post %s: %s",cvpost.U,keys))
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
// c.IndentedJSON(http.StatusCreated, cvdata)
|
||||
}
|
108
handlers_auth.go
Normal file
108
handlers_auth.go
Normal file
@ -0,0 +1,108 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
b64 "encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
)
|
||||
|
||||
func get_auth_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
id := c.Params.ByName(rtenv.Cfg.Routes["auth"].Param)
|
||||
pass := ""
|
||||
role := rtenv.Cfg.AdminRole
|
||||
tkn := "-"
|
||||
hasRole := false
|
||||
var err error
|
||||
if strings.Contains(id,rtenv.Cfg.AuthSep) {
|
||||
s := strings.Split(id,rtenv.Cfg.AuthSep)
|
||||
id = s[0]
|
||||
pasw,_ := b64.StdEncoding.DecodeString(s[1])
|
||||
pass = string(pasw)
|
||||
}
|
||||
mdlUsr, okmdl := rtenv.MdlsUsrs[id]
|
||||
data := &User{
|
||||
UserName: id,
|
||||
UUID: id,
|
||||
Data: "",
|
||||
FirstName: "",
|
||||
LastName: "",
|
||||
}
|
||||
if okmdl {
|
||||
if val, ok := rtenv.Users.Accounts[mdlUsr.User]; ok {
|
||||
if len(pass) == 0 {
|
||||
c.JSON(http.StatusOK, gin.H{"auth": "?"})
|
||||
return
|
||||
}
|
||||
txtdata := ""
|
||||
txtdata,err = decrypt(val.Passwd, string(CRYPTKEY))
|
||||
if txtdata != pass {
|
||||
c.JSON(http.StatusOK, gin.H{"auth": "?"})
|
||||
return
|
||||
}
|
||||
}
|
||||
hasRole,_ = rtenv.Enforcer.HasRoleForUser(mdlUsr.User, role)
|
||||
data.UserName = mdlUsr.User
|
||||
data.Data = mdlUsr.Data
|
||||
data.FirstName = id
|
||||
logRoute(c,rtenv,"auth",fmt.Sprintf("get %s %s", rtenv.Cfg.Routes["auth"].Path,id),fmt.Sprintf("get %s (%s %v) %s",mdlUsr.User,role,hasRole,tkn))
|
||||
} else {
|
||||
logRoute(c,rtenv,"auth",fmt.Sprintf("get %s %s", rtenv.Cfg.Routes["auth"].Path,id),fmt.Sprintf("get %s (%s %v) %s","-",role,hasRole,tkn))
|
||||
}
|
||||
if rtenv.Cfg.UseJWT {
|
||||
if rtenv.AuthMiddleware == nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to auth"})
|
||||
return
|
||||
}
|
||||
tkn,err = makeTokenString(rtenv,data)
|
||||
if err != nil {
|
||||
fmt.Printf("tkn err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to auth"})
|
||||
return
|
||||
}
|
||||
fmt.Printf("tkn: %+v\n", tkn)
|
||||
}
|
||||
if rtenv.Cfg.UseJWT {
|
||||
c.JSON(http.StatusOK, gin.H{"auth": tkn, "user": mdlUsr.User, "model": mdlUsr.Model, "data": mdlUsr.Data, "hasrole": hasRole})
|
||||
} else {
|
||||
c.JSON(http.StatusOK, gin.H{"pass": pass, "user": mdlUsr.User, "model": mdlUsr.Model, "data": mdlUsr.Data, "hasrole": hasRole})
|
||||
}
|
||||
}
|
||||
|
||||
func get_auth_refresh_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
// token,expire,err := refreshToken(c,rtenv,data interface{}) (string, time.Time, error) {
|
||||
claims, err := rtenv.AuthMiddleware.CheckIfTokenExpire(c)
|
||||
if err != nil {
|
||||
fmt.Printf("Error Refresh token: %v\n",err)
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Token is expire"})
|
||||
return
|
||||
}
|
||||
var tkn string
|
||||
data := &User{
|
||||
UserName: "",
|
||||
UUID: "",
|
||||
Data: "",
|
||||
FirstName: "",
|
||||
LastName: "",
|
||||
}
|
||||
fmt.Printf("Refresh token: %v\n",claims)
|
||||
if val, ok := claims["id"]; ok {
|
||||
data.UserName = fmt.Sprintf("%s",val)
|
||||
}
|
||||
if val, ok := claims["uuid"]; ok {
|
||||
data.UUID = fmt.Sprintf("%s",val)
|
||||
}
|
||||
if val, ok := claims["data"]; ok {
|
||||
data.Data = fmt.Sprintf("%s",val)
|
||||
}
|
||||
tkn,err = makeTokenString(rtenv,data)
|
||||
if err != nil {
|
||||
fmt.Printf("tkn err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to auth"})
|
||||
return
|
||||
}
|
||||
fmt.Printf("tkn: %+v\n", tkn)
|
||||
c.JSON(http.StatusOK, gin.H{"auth": tkn, "user": data.UserName, "uuid": data.UUID, "data": data.Data })
|
||||
}
|
82
handlers_config.go
Normal file
82
handlers_config.go
Normal file
@ -0,0 +1,82 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
utils "github.com/jesusperez/datautils"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
func get_config_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
idusr := rtenv.AuthMiddleware.IdentityHandler(c)
|
||||
user, okusr := idusr.(*User)
|
||||
if !okusr || len(user.UserName) == 0 {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
role := rtenv.Cfg.AdminRole
|
||||
if _,res := user_has_role(user,c,rtenv,role); !res {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
path := fmt.Sprintf("%s_new.yaml",strings.Replace(rtenv.RunFlags.cfgPath,".yaml","",-1))
|
||||
if utils.ExistsPath(path) {
|
||||
config,err := loadConfig(path)
|
||||
if err != nil {
|
||||
logRoute(c,rtenv,"config",fmt.Sprintf("get /config error %v",err),fmt.Sprintf("get config_new %s (%s)",user.UserName,role))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load config_new"})
|
||||
return
|
||||
}
|
||||
logRoute(c,rtenv,"config","get /config",fmt.Sprintf("get config_new %s (%s)",user.UserName,role))
|
||||
c.JSON(http.StatusOK, config)
|
||||
} else {
|
||||
logRoute(c,rtenv,"config","get /config",fmt.Sprintf("get config %s (%s)",user.UserName,role))
|
||||
c.JSON(http.StatusOK, rtenv.Cfg)
|
||||
}
|
||||
}
|
||||
|
||||
func post_config_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
var dataconfig ConfigPostData
|
||||
role := rtenv.Cfg.AdminRole
|
||||
err := c.BindJSON(&dataconfig)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save config"})
|
||||
return
|
||||
}
|
||||
if len(dataconfig.Id) == 0 || len(dataconfig.Config.Routes) == 0 {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"config": "error config"})
|
||||
return
|
||||
}
|
||||
// roles,_ := enforcer.GetRolesForUser(user)
|
||||
hasRole,_ := rtenv.Enforcer.HasRoleForUser(dataconfig.Id, role)
|
||||
fmt.Printf("%s (%s) %+v\n",dataconfig.Id, role, hasRole)
|
||||
if !hasRole {
|
||||
logRoute(c,rtenv,"post_config",fmt.Sprintf("post %s: has no role",dataconfig.Id), fmt.Sprintf("post %s: %s",dataconfig.Id,dataconfig.Val))
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
data, err := yaml.Marshal(dataconfig.Config)
|
||||
if err != nil {
|
||||
logRoute(c,rtenv,"post_config",fmt.Sprintf("post %s: error %v ",dataconfig.Id,err), fmt.Sprintf("post %s: %s",dataconfig.Id,dataconfig.Val))
|
||||
fmt.Printf("Error Parse dataconfig %s (%s): %v\n", dataconfig.Id, dataconfig.Val, err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save config"})
|
||||
return
|
||||
}
|
||||
path := fmt.Sprintf("%s.yaml",dataconfig.Val)
|
||||
if path == rtenv.RunFlags.cfgPath {
|
||||
path = fmt.Sprintf("%s_new.yaml",dataconfig.Val)
|
||||
}
|
||||
if res := utils.WriteData(string(data),path) ; res != nil {
|
||||
logRoute(c,rtenv,"post_config",fmt.Sprintf("post %s: write error %v ",dataconfig.Id,err), fmt.Sprintf("post %s: %s",dataconfig.Id,dataconfig.Val))
|
||||
fmt.Printf("Error write config from %s to %s: %v\n", dataconfig.Id, path, err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save config"})
|
||||
} else {
|
||||
fmt.Printf("Config from %s writed to: %s\n", dataconfig.Id, path)
|
||||
logRoute(c,rtenv,"post_config",fmt.Sprintf("post %s: %s",dataconfig.Id,dataconfig.Val), fmt.Sprintf("post %s: %s",dataconfig.Id,dataconfig.Val))
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
}
|
||||
}
|
347
handlers_mng_users.go
Normal file
347
handlers_mng_users.go
Normal file
@ -0,0 +1,347 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
b64 "encoding/base64"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
utils "github.com/jesusperez/datautils"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
func send_invitation_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
role := rtenv.Cfg.AdminRole
|
||||
user,res := user_has_role(nil,c,rtenv,role)
|
||||
if !res {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
var sendData PostInvitation
|
||||
err := c.BindJSON(&sendData)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save info"})
|
||||
return
|
||||
}
|
||||
if len(sendData.Data.Email) == 0 {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": "no data found"})
|
||||
return
|
||||
}
|
||||
accept_langs := strings.Split(c.Request.Header.Get("Accept-Language"),";")
|
||||
langs := strings.Split(accept_langs[0], ",")
|
||||
tplPath := fmt.Sprintf("%s/%s_%s",rtenv.Cfg.TplsMailPath,langs[0],rtenv.Cfg.TplsMail["newuser"].Path)
|
||||
if ! utils.ExistsPath(tplPath) {
|
||||
tplPath = fmt.Sprintf("%s/%s",rtenv.Cfg.TplsMailPath,rtenv.Cfg.TplsMail["newuser"].Path)
|
||||
if ! utils.ExistsPath(tplPath) {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": "no template found"})
|
||||
return
|
||||
}
|
||||
}
|
||||
target := rtenv.Cfg.MailFrom
|
||||
bodyData := sendData
|
||||
if len(sendData.Data.Expire) > 0 {
|
||||
bodyData.Data.Expire = fmt.Sprintf("Expire: %s",sendData.Data.Expire)
|
||||
}
|
||||
request := Mail{
|
||||
Sender: target,
|
||||
To: []string{sendData.Data.Email},
|
||||
Cc: []string{},
|
||||
Bcc: []string{},
|
||||
Subject: fmt.Sprintf("Invitation "),
|
||||
Body: bytes.Buffer{},
|
||||
}
|
||||
err = email(rtenv.Cfg, "text", tplPath, "", "", request,bodyData)
|
||||
if err == nil {
|
||||
fmt.Printf("Invitation send from (%s): %s\n",user.UserName, target)
|
||||
logRoute(c,rtenv,"send_inivitation",fmt.Sprintf("get %s: %s",user.UserName,target), fmt.Sprintf("get %s: %s",user.UserName,target))
|
||||
c.JSON(http.StatusOK, sendData)
|
||||
} else {
|
||||
fmt.Printf("Error send invitation from (%s) %s: %v\n", user.UserName, target, err)
|
||||
logRoute(c,rtenv,"send_invitation",fmt.Sprintf("get %s: read error %v ",user.UserName,err), fmt.Sprintf("get %s: %s",user.UserName,target))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to send invitation"})
|
||||
}
|
||||
}
|
||||
func get_invitation_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
id := c.Params.ByName(rtenv.Cfg.Routes["invitation"].Param)
|
||||
usrsInvitations,err := getUsersInvitations(rtenv.Cfg.InvitationsPath,true)
|
||||
if err != nil {
|
||||
logRoute(c,rtenv,"users",fmt.Sprintf("get /invitation error %v",err),fmt.Sprintf("get invitation %s",id))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to get invitation %s",id)})
|
||||
return
|
||||
}
|
||||
if val, ok := usrsInvitations[id]; ok {
|
||||
invitation, valid, needSync := checkUserInvitation(id, usrsInvitations[id], 1)
|
||||
if (needSync) {
|
||||
allInvitations, err := loadUsersInvitations(rtenv.Cfg.InvitationsPath)
|
||||
if err != nil {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to validate invitation %s",id)})
|
||||
return
|
||||
}
|
||||
allInvitations[id]=invitation
|
||||
strdata, yamlerr := yaml.Marshal(allInvitations)
|
||||
if yamlerr != nil {
|
||||
fmt.Printf("Error parse invitations yaml %s: %v",rtenv.Cfg.InvitationsPath,yamlerr)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to sync invitations %s",id)})
|
||||
return
|
||||
}
|
||||
if res := utils.WriteData(string(strdata),rtenv.Cfg.InvitationsPath) ; res != nil {
|
||||
logRoute(c,rtenv,"post_users",fmt.Sprintf("Error write invitations: %s",id), fmt.Sprintf("error: %+v",res))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to sync invitation %s",id)})
|
||||
return
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
logRoute(c,rtenv,"users",fmt.Sprintf("get /invitation error %v",err),fmt.Sprintf("get invitation not valid %s",id))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to validate invitation %s",id)})
|
||||
return
|
||||
}
|
||||
tkn := ""
|
||||
if rtenv.Cfg.UseAuthz {
|
||||
data := &User{
|
||||
UserName: val.Email,
|
||||
UUID: id,
|
||||
Data: "Invitation",
|
||||
FirstName: "",
|
||||
LastName: "",
|
||||
}
|
||||
tkn,err = makeTokenString(rtenv,data)
|
||||
if err != nil {
|
||||
fmt.Printf("tkn err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to auth"})
|
||||
return
|
||||
}
|
||||
}
|
||||
logRoute(c,rtenv,"users",fmt.Sprintf("get /invitation %s",id),fmt.Sprintf("get invitation %s (%s) %s",val.Email,val.Role,val.Data))
|
||||
data := NewUserData{ Email: val.Email, Role: val.Role, Data: val.Data }
|
||||
strdata,err := json.Marshal(data)
|
||||
if err == nil {
|
||||
c.JSON(http.StatusOK, gin.H{"data": b64.StdEncoding.EncodeToString(strdata),"auth": tkn})
|
||||
return
|
||||
} else {
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to confirm invitation %s",id)})
|
||||
return
|
||||
}
|
||||
} else {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Invitation not found"})
|
||||
}
|
||||
}
|
||||
func get_users_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
idusr := rtenv.AuthMiddleware.IdentityHandler(c)
|
||||
user, okusr := idusr.(*User)
|
||||
if !okusr || len(user.UserName) == 0 {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
role := rtenv.Cfg.AdminRole
|
||||
if _,res := user_has_role(user,c,rtenv,role); !res {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
usersData := &UsersData {
|
||||
UsersData: "",
|
||||
ModelsData: "",
|
||||
Invitations: "",
|
||||
AuthzModel: "",
|
||||
AuthzPolicy: "",
|
||||
}
|
||||
if utils.ExistsPath(rtenv.Cfg.UsersPath) {
|
||||
data,err := loadPath(rtenv.Cfg.UsersPath)
|
||||
if err != nil {
|
||||
logRoute(c,rtenv,"config",fmt.Sprintf("get /users error %v",err),fmt.Sprintf("get userspath %s (%s)",user.UserName,role))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load users data"})
|
||||
return
|
||||
}
|
||||
usersData.UsersData = b64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
if utils.ExistsPath(rtenv.Cfg.UsersModelsPath) {
|
||||
data,err := loadPath(rtenv.Cfg.UsersModelsPath)
|
||||
if err != nil {
|
||||
logRoute(c,rtenv,"config",fmt.Sprintf("get /users error %v",err),fmt.Sprintf("get usersmodels %s (%s)",user.UserName,role))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load users models"})
|
||||
return
|
||||
}
|
||||
usersData.ModelsData = b64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
if utils.ExistsPath(rtenv.Cfg.InvitationsPath) {
|
||||
data,err := loadPath(rtenv.Cfg.InvitationsPath)
|
||||
if err != nil {
|
||||
logRoute(c,rtenv,"config",fmt.Sprintf("get /users error %v",err),fmt.Sprintf("get invitations %s (%s)",user.UserName,role))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load users invitations"})
|
||||
return
|
||||
}
|
||||
usersData.Invitations = b64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
if rtenv.Cfg.UseAuthz {
|
||||
if utils.ExistsPath(rtenv.Cfg.AuthzModel) {
|
||||
data,err := loadPath(rtenv.Cfg.AuthzModel)
|
||||
if err != nil {
|
||||
logRoute(c,rtenv,"config",fmt.Sprintf("get /users error %v",err),fmt.Sprintf("get authzmodels %s (%s)",user.UserName,role))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load authz models"})
|
||||
return
|
||||
}
|
||||
usersData.AuthzModel = b64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
if utils.ExistsPath(rtenv.Cfg.AuthzPolicy) {
|
||||
data,err := loadPath(rtenv.Cfg.AuthzPolicy)
|
||||
if err != nil {
|
||||
logRoute(c,rtenv,"config",fmt.Sprintf("get /users error %v",err),fmt.Sprintf("get authzpolizy %s (%s)",user.UserName,role))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to load authz policy"})
|
||||
return
|
||||
}
|
||||
usersData.AuthzPolicy = b64.StdEncoding.EncodeToString(data)
|
||||
}
|
||||
}
|
||||
logRoute(c,rtenv,"config","get /users",fmt.Sprintf("get users %s (%s)",user.UserName,role))
|
||||
c.JSON(http.StatusOK, gin.H{"data": usersData})
|
||||
}
|
||||
func save_users_data(saveInfo SaveDataInfo,c *gin.Context,rtenv *RouteEnv) bool {
|
||||
path := fmt.Sprintf("%s%s",saveInfo.Prfx, saveInfo.Path)
|
||||
data,err := b64.StdEncoding.DecodeString(saveInfo.Data)
|
||||
if err != nil {
|
||||
fmt.Printf("Error save %s: %v",path,err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to save %s",saveInfo.Title)})
|
||||
return false
|
||||
}
|
||||
var accounts map[string]Account
|
||||
err = yaml.Unmarshal([]byte(data), &accounts)
|
||||
if err != nil {
|
||||
fmt.Printf("Error parse yaml %s: %v",path,err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to save %s",saveInfo.Title)})
|
||||
return false
|
||||
}
|
||||
prfx := rtenv.Cfg.PasswdEnc
|
||||
var allUsers = map[string]Account{}
|
||||
for key, item := range accounts {
|
||||
if strings.HasPrefix(item.Passwd,"enc:") {
|
||||
txtPasswd := strings.Replace(item.Passwd,prfx,"",-1)
|
||||
passwd,err := encrypt(txtPasswd, string(CRYPTKEY))
|
||||
if err == nil {
|
||||
item.Passwd = passwd
|
||||
allUsers[key] = item
|
||||
}
|
||||
} else {
|
||||
allUsers[key] = item
|
||||
}
|
||||
}
|
||||
strdata, yamlerr := yaml.Marshal(allUsers)
|
||||
if yamlerr != nil {
|
||||
fmt.Printf("Error parse yaml %s: %v",path,yamlerr)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to save %s",saveInfo.Title)})
|
||||
return false
|
||||
}
|
||||
if res := utils.WriteData(string(strdata),path) ; res != nil {
|
||||
logRoute(c,rtenv,"post_users",fmt.Sprintf("Error post %s: %s",saveInfo.Title,saveInfo.Id), fmt.Sprintf("error: %+v",res))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to save %s",saveInfo.Title)})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func save_file_data(saveInfo SaveDataInfo,c *gin.Context,rtenv *RouteEnv) bool {
|
||||
path := fmt.Sprintf("%s%s",saveInfo.Prfx, saveInfo.Path)
|
||||
data,err := b64.StdEncoding.DecodeString(saveInfo.Data)
|
||||
if err != nil {
|
||||
fmt.Printf("Error save %s: %v",path,err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to save %s",saveInfo.Title)})
|
||||
return false
|
||||
}
|
||||
if res := utils.WriteData(string(data),path) ; res != nil {
|
||||
logRoute(c,rtenv,"post_users",fmt.Sprintf("Error post %s: %s",saveInfo.Title,saveInfo.Id), fmt.Sprintf("error: %+v",res))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to save %s",saveInfo.Title)})
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
func post_users_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
var postdata UsersDataPost
|
||||
role := rtenv.Cfg.AdminRole
|
||||
err := c.BindJSON(&postdata)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save users"})
|
||||
return
|
||||
}
|
||||
if len(postdata.Id) == 0 {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"info": "error users"})
|
||||
return
|
||||
}
|
||||
hasRole,_ := rtenv.Enforcer.HasRoleForUser(postdata.Id,role)
|
||||
// fmt.Printf("post users: %s (%s) %+v\n",postdata.Id, role, hasRole)
|
||||
if !hasRole {
|
||||
logRoute(c,rtenv,"post_users",fmt.Sprintf("post %s: has no role",postdata.Id), fmt.Sprintf("post %s",postdata.Id))
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
savedList := ""
|
||||
if len(postdata.Data.UsersData) > 0 {
|
||||
saveInfo := SaveDataInfo{
|
||||
Title: "users",
|
||||
Data: postdata.Data.UsersData,
|
||||
Id: postdata.Id,
|
||||
Path: rtenv.Cfg.UsersPath,
|
||||
Prfx: "new_",
|
||||
}
|
||||
if !save_users_data(saveInfo,c,rtenv) {
|
||||
return
|
||||
}
|
||||
savedList = "usersData"
|
||||
}
|
||||
if len(postdata.Data.ModelsData) > 0 {
|
||||
saveInfo := SaveDataInfo{
|
||||
Title: "users models",
|
||||
Data: postdata.Data.ModelsData,
|
||||
Id: postdata.Id,
|
||||
Path: rtenv.Cfg.UsersModelsPath,
|
||||
Prfx: "new_",
|
||||
}
|
||||
if !save_file_data(saveInfo,c,rtenv) {
|
||||
return
|
||||
}
|
||||
savedList = fmt.Sprintf("%s,modelsData",savedList)
|
||||
}
|
||||
if len(postdata.Data.Invitations) > 0 {
|
||||
saveInfo := SaveDataInfo{
|
||||
Title: "users invitations",
|
||||
Data: postdata.Data.Invitations,
|
||||
Id: postdata.Id,
|
||||
Path: rtenv.Cfg.InvitationsPath,
|
||||
Prfx: "new_",
|
||||
}
|
||||
if !save_file_data(saveInfo,c,rtenv) {
|
||||
return
|
||||
}
|
||||
savedList = fmt.Sprintf("%s,invitations",savedList)
|
||||
}
|
||||
if rtenv.Cfg.UseAuthz {
|
||||
if len(postdata.Data.AuthzModel) > 0 {
|
||||
saveInfo := SaveDataInfo{
|
||||
Title: "users authz models",
|
||||
Data: postdata.Data.AuthzModel,
|
||||
Id: postdata.Id,
|
||||
Path: rtenv.Cfg.AuthzModel,
|
||||
Prfx: "new_",
|
||||
}
|
||||
if !save_file_data(saveInfo,c,rtenv) {
|
||||
return
|
||||
}
|
||||
savedList = fmt.Sprintf("%s,authzModels",savedList)
|
||||
}
|
||||
if len(postdata.Data.AuthzPolicy) > 0 {
|
||||
saveInfo := SaveDataInfo{
|
||||
Title: "users authz policy",
|
||||
Data: postdata.Data.AuthzPolicy,
|
||||
Id: postdata.Id,
|
||||
Path: rtenv.Cfg.AuthzPolicy,
|
||||
Prfx: "new_",
|
||||
}
|
||||
if !save_file_data(saveInfo,c,rtenv) {
|
||||
return
|
||||
}
|
||||
savedList = fmt.Sprintf("%s,authzPolicy",savedList)
|
||||
}
|
||||
}
|
||||
logRoute(c,rtenv,"post_users",fmt.Sprintf("post %s: users",postdata.Id), fmt.Sprintf("post %s: %s",postdata.Id,savedList))
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
}
|
153
handlers_tracking.go
Normal file
153
handlers_tracking.go
Normal file
@ -0,0 +1,153 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
cfg "github.com/jesusperez/cfgsrv"
|
||||
utils "github.com/jesusperez/datautils"
|
||||
)
|
||||
func get_tracking_list_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
role := rtenv.Cfg.AdminRole
|
||||
user,res := user_has_role(nil,c,rtenv,role)
|
||||
if !res {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
var trackingData []string
|
||||
result := false
|
||||
path := ""
|
||||
var err error
|
||||
switch (rtenv.Cfg.TrackingStore) {
|
||||
case cfg.DsRedis:
|
||||
path = "redis"
|
||||
// TODO send to redis client
|
||||
case cfg.DsFile:
|
||||
path = rtenv.Cfg.TrackingOut
|
||||
trackingData = getFileMatch(path,".json",false,false)
|
||||
result = true
|
||||
}
|
||||
if result {
|
||||
fmt.Printf("Tracking actions from (%s): %s\n",user.UserName, path)
|
||||
logRoute(c,rtenv,"get_tracking",fmt.Sprintf("get %s: %s",user.UserName,path), fmt.Sprintf("get %s: %s",user.UserName,path))
|
||||
// c.JSON(http.StatusOK, gin.H{"data": trackingData})
|
||||
c.JSON(http.StatusOK, trackingData)
|
||||
} else {
|
||||
logRoute(c,rtenv,"get_tracking",fmt.Sprintf("get %s: read error %v ",user.UserName,err), fmt.Sprintf("get %s: %s",user.UserName,path))
|
||||
fmt.Printf("Error get tracking from (%s) %s: %v\n", user.UserName, path, err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read tracking list"})
|
||||
}
|
||||
}
|
||||
func get_tracking_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
role := rtenv.Cfg.AdminRole
|
||||
user,res := user_has_role(nil,c,rtenv,role)
|
||||
if !res {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
target := c.Params.ByName(rtenv.Cfg.Routes["tracking"].Param)
|
||||
var trackingData []TrackAction
|
||||
result := false
|
||||
path := ""
|
||||
var err error
|
||||
switch (rtenv.Cfg.TrackingStore) {
|
||||
case cfg.DsRedis:
|
||||
path = "redis"
|
||||
// TODO send to redis client
|
||||
case cfg.DsFile:
|
||||
path = fmt.Sprintf("%s/%s.json",rtenv.Cfg.TrackingOut,target)
|
||||
var data []byte
|
||||
data, err = loadPath(path)
|
||||
if err == nil {
|
||||
err = json.Unmarshal([]byte(fmt.Sprintf("[%s{}]",string(data))), &trackingData)
|
||||
if err == nil {
|
||||
result = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if result {
|
||||
fmt.Printf("Tracking actions from (%s): %s\n",user.UserName, path)
|
||||
logRoute(c,rtenv,"get_tracking",fmt.Sprintf("get %s: %s",user.UserName,path), fmt.Sprintf("get %s: %s",user.UserName,path))
|
||||
// c.JSON(http.StatusOK, gin.H{"data": trackingData})
|
||||
c.JSON(http.StatusOK, trackingData)
|
||||
} else {
|
||||
logRoute(c,rtenv,"get_tracking",fmt.Sprintf("get %s: read error %v ",user.UserName,err), fmt.Sprintf("get %s: %s",user.UserName,path))
|
||||
fmt.Printf("Error get tracking from (%s) %s: %v\n", user.UserName, path, err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to read tracking"})
|
||||
}
|
||||
}
|
||||
|
||||
func post_tracking_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
var trackpost PostTrackAction
|
||||
err := c.BindJSON(&trackpost)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save info"})
|
||||
return
|
||||
}
|
||||
if len(trackpost.Data) == 0 {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"track": "error data"})
|
||||
return
|
||||
}
|
||||
userid := trackpost.Data
|
||||
_, okmdl := rtenv.MdlsUsrs[userid]
|
||||
if !okmdl {
|
||||
_, okusr := rtenv.Users.Accounts[userid]
|
||||
if !okusr {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
}
|
||||
auth := ""
|
||||
if rtenv.Cfg.UseJWT {
|
||||
tk, errtk := rtenv.AuthMiddleware.ParseToken(c)
|
||||
if errtk == nil {
|
||||
auth = fmt.Sprintf("%v",tk.Raw)
|
||||
}
|
||||
}
|
||||
trackaction := &TrackAction{
|
||||
When: trackpost.When,
|
||||
Where: trackpost.Where,
|
||||
What: trackpost.What,
|
||||
Context: trackpost.Context,
|
||||
Data: trackpost.Data,
|
||||
Auth: auth,
|
||||
}
|
||||
data,err := json.Marshal(trackaction)
|
||||
if err != nil {
|
||||
logRoute(c,rtenv,"post_trackng",fmt.Sprintf("post %s: error %v ",trackpost.Data,err), fmt.Sprintf("post %s",userid))
|
||||
fmt.Printf("Error Parse tracking %s: %v\n", trackpost.Data, err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save tracking"})
|
||||
return
|
||||
}
|
||||
result := false
|
||||
path := ""
|
||||
switch (rtenv.Cfg.TrackingStore) {
|
||||
case cfg.DsRedis:
|
||||
path = "redis"
|
||||
// TODO send to redis client
|
||||
case cfg.DsFile:
|
||||
path = fmt.Sprintf("%s/%s.json",rtenv.Cfg.TrackingOut,time.Now().Format("2006_01_02"))
|
||||
err := utils.CheckDirPath(fmt.Sprintf("%s",rtenv.Cfg.TrackingOut))
|
||||
if err != nil {
|
||||
log.Fatalf("setReqLog path %s: %v",rtenv.Cfg.TrackingOut,err)
|
||||
}
|
||||
err = utils.WriteAppendData(fmt.Sprintf("%s,",string(data)),path)
|
||||
if err == nil {
|
||||
result = true
|
||||
}
|
||||
}
|
||||
if result {
|
||||
fmt.Printf("Track action from %s writed to: %s\n", userid, path)
|
||||
logRoute(c,rtenv,"post_tracking",fmt.Sprintf("post %s: %s",userid,path), fmt.Sprintf("post %s: %s",userid,path))
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
} else {
|
||||
logRoute(c,rtenv,"post_tracking",fmt.Sprintf("post %s: write error %v ",userid,err), fmt.Sprintf("post %s: %s",userid,path))
|
||||
fmt.Printf("Error write tracking from %s to %s: %v\n", userid, path, err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save tracking"})
|
||||
}
|
||||
}
|
238
handlers_user.go
Normal file
238
handlers_user.go
Normal file
@ -0,0 +1,238 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
b64 "encoding/base64"
|
||||
"fmt"
|
||||
"net/http"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/google/uuid"
|
||||
|
||||
cvdata "github.com/jesusperez/cvdata"
|
||||
utils "github.com/jesusperez/datautils"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
func create_password(val string) (string,error) {
|
||||
txtVal,err := b64.StdEncoding.DecodeString(val)
|
||||
if err != nil {
|
||||
return "",err
|
||||
}
|
||||
pswd,err := encrypt(string(txtVal), string(CRYPTKEY))
|
||||
if err != nil {
|
||||
return "",err
|
||||
}
|
||||
return pswd,nil
|
||||
}
|
||||
func write_accounts(newAccounts map[string]Account,rtenv *RouteEnv) error {
|
||||
strdata, err := yaml.Marshal(newAccounts)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if res := utils.WriteData(string(strdata),rtenv.Cfg.UsersPath) ; res != nil {
|
||||
return res
|
||||
}
|
||||
acc,err := loadUsersAccounts(rtenv.Cfg.UsersPath)
|
||||
if err != nil {
|
||||
return err
|
||||
} else {
|
||||
rtenv.Users = acc
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func get_recoveryaccess_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
// fmt.Printf("context: %+v\n", c)
|
||||
target := c.Params.ByName(rtenv.Cfg.Routes["recoveryaccess"].Param)
|
||||
if target == "-" {
|
||||
target = "main"
|
||||
}
|
||||
var user Account
|
||||
if val, ok := rtenv.Users.Accounts[target]; ok {
|
||||
user = val
|
||||
} else {
|
||||
fmt.Printf("Error Recovery access for (%s) %s\n", user.Id, target)
|
||||
logRoute(c,rtenv,"Recovery access send_invitation",fmt.Sprintf("get %s: read error",user.Id), fmt.Sprintf("get %s: %s",user.Id,target))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed recovery request"})
|
||||
return
|
||||
}
|
||||
uuid := uuid.NewString()
|
||||
uuid = strings.Replace(uuid,"-","",-1)
|
||||
appDB[uuid]=AppData{
|
||||
Data: user,
|
||||
Expire: time.Now().Add(5*time.Minute).Format(time.RFC3339),
|
||||
}
|
||||
reqData := UsersRecover{
|
||||
Id: uuid,
|
||||
Data: user,
|
||||
}
|
||||
accept_langs := strings.Split(c.Request.Header.Get("Accept-Language"),";")
|
||||
langs := strings.Split(accept_langs[0], ",")
|
||||
tplPath := fmt.Sprintf("%s/%s_%s",rtenv.Cfg.TplsMailPath,langs[0],rtenv.Cfg.TplsMail["recovery"].Path)
|
||||
if ! utils.ExistsPath(tplPath) {
|
||||
tplPath = fmt.Sprintf("%s/%s",rtenv.Cfg.TplsMailPath,rtenv.Cfg.TplsMail["recovery"].Path)
|
||||
if ! utils.ExistsPath(tplPath) {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"error": "no template found"})
|
||||
return
|
||||
}
|
||||
}
|
||||
request := Mail{
|
||||
Sender: rtenv.Cfg.MailFrom,
|
||||
To: []string{user.Email},
|
||||
Cc: []string{},
|
||||
Bcc: []string{},
|
||||
Subject: fmt.Sprintf("Recovery access"),
|
||||
Body: bytes.Buffer{},
|
||||
}
|
||||
err := email(rtenv.Cfg, "text", tplPath, "", "", request,reqData)
|
||||
if err == nil {
|
||||
fmt.Printf("Recovery access for (%s): %s\n",user.Id, target)
|
||||
logRoute(c,rtenv,"Recovery access",fmt.Sprintf("get %s: %s",user.Id,target), fmt.Sprintf("get %s: %s",user.Id,target))
|
||||
c.JSON(http.StatusOK, gin.H{"received": "ok"})
|
||||
} else {
|
||||
fmt.Printf("Error Recovery access for (%s) %s: %v\n", user.Id, target, err)
|
||||
logRoute(c,rtenv,"Recovery access send_invitation",fmt.Sprintf("get %s: read error %v ",user.Id,err), fmt.Sprintf("get %s: %s",user.Id,target))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed recovery request"})
|
||||
}
|
||||
}
|
||||
func post_recoveryaccess_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
var postdata UsersRecoveryPost
|
||||
err := c.BindJSON(&postdata)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed recovery user"})
|
||||
return
|
||||
}
|
||||
if len(postdata.Id) == 0 {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"info": "error recovery user"})
|
||||
return
|
||||
}
|
||||
valid := false
|
||||
var user Account
|
||||
if data, ok := appDB[postdata.Id]; ok {
|
||||
user = data.Data.(Account)
|
||||
res,err := time.Parse(time.RFC3339,data.Expire)
|
||||
if err == nil {
|
||||
if res.After(time.Now()) {
|
||||
valid = true
|
||||
}
|
||||
}
|
||||
}
|
||||
if !valid {
|
||||
fmt.Printf("Error Recovery access for %s\n", user.Id)
|
||||
logRoute(c,rtenv,"Recovery access send_invitation",fmt.Sprintf("get %s: read error %v ",user.Id,err), fmt.Sprintf("get %s",user.Id))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed recovery request"})
|
||||
return
|
||||
}
|
||||
accounts,err := loadUsersData(rtenv.Cfg.UsersPath)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed recovery user"})
|
||||
return
|
||||
}
|
||||
if account, ok := accounts[user.Id]; ok {
|
||||
newAccounts := accounts
|
||||
pswd,err := create_password(postdata.Val)
|
||||
if err != nil {
|
||||
fmt.Printf("Error data: %v",err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed user data %s",user.Id)})
|
||||
}
|
||||
account.Passwd = pswd
|
||||
newAccounts[user.Id] = account
|
||||
errwrite := write_accounts(newAccounts,rtenv)
|
||||
if err != nil {
|
||||
logRoute(c,rtenv,"post_recoveryaccess",fmt.Sprintf("Error post save recoveryaccess %s",user.Id), fmt.Sprintf("error: %+v",errwrite))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to save user data")})
|
||||
return
|
||||
}
|
||||
delete(appDB,postdata.Id)
|
||||
logRoute(c,rtenv,"post_recoveryaccess",fmt.Sprintf("post %s: users",postdata.Id), fmt.Sprintf("post recovery access: %s",user.Id))
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
} else {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed recovery user"})
|
||||
return
|
||||
}
|
||||
}
|
||||
func post_newuser_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
var postdata NewUserData
|
||||
err := c.BindJSON(&postdata)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save info"})
|
||||
return
|
||||
}
|
||||
fmt.Printf("postdata: %+v\n", postdata)
|
||||
if len(postdata.Username) == 0 || len(postdata.Passwd) == 0 {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"info": "error info"})
|
||||
return
|
||||
}
|
||||
pswd, err := create_password(postdata.Passwd)
|
||||
if err != nil {
|
||||
fmt.Printf("Error data: %v",err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed user data %s",postdata.Username)})
|
||||
}
|
||||
newUser := Account{
|
||||
Username: postdata.Username,
|
||||
Email: postdata.Email,
|
||||
Passwd: pswd,
|
||||
Data: postdata.Username,
|
||||
Web: true,
|
||||
Id: postdata.Username,
|
||||
}
|
||||
newAccounts,err := loadUsersData(rtenv.Cfg.UsersPath)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed recovery user"})
|
||||
return
|
||||
}
|
||||
newAccounts[postdata.Username] = newUser
|
||||
errwrite := write_accounts(newAccounts,rtenv)
|
||||
if err != nil {
|
||||
logRoute(c,rtenv,"post_newuser",fmt.Sprintf("Error post save newuser %s",postdata.Username), fmt.Sprintf("error: %+v",errwrite))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("Failed to save user data")})
|
||||
return
|
||||
}
|
||||
logRoute(c,rtenv,"post_newuser",fmt.Sprintf("post newuser: %s",postdata.Username), fmt.Sprintf("post newuser: %s",postdata.Username))
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
// c.IndentedJSON(http.StatusCreated, cvdata)
|
||||
}
|
||||
|
||||
func post_usermodel_handle(c *gin.Context, rtenv *RouteEnv) {
|
||||
var cvpost cvdata.CVPostData
|
||||
role := rtenv.Cfg.AdminRole
|
||||
err := c.BindJSON(&cvpost)
|
||||
if err != nil {
|
||||
fmt.Printf("err: %+v\n", err)
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save info"})
|
||||
return
|
||||
}
|
||||
if cvpost.U == "" || len(cvpost.Data) == 0 {
|
||||
c.JSON(http.StatusNotAcceptable, gin.H{"info": "error info"})
|
||||
return
|
||||
}
|
||||
// roles,_ := enforcer.GetRolesForUser(user)
|
||||
hasRole,_ := rtenv.Enforcer.HasRoleForUser(cvpost.U, role)
|
||||
fmt.Printf("%s (%s) %+v\n",cvpost.U, role, hasRole)
|
||||
if !hasRole {
|
||||
c.JSON(http.StatusUnauthorized, gin.H{"error": "Authentication failed"})
|
||||
return
|
||||
}
|
||||
keys,res := cvpost.Data.Write(rtenv.Cfg)
|
||||
if res != nil {
|
||||
logRoute(c,rtenv,"post_newuser",fmt.Sprintf("Error post %s: %s",cvpost.U,keys), fmt.Sprintf("error: %+v",res))
|
||||
c.JSON(http.StatusInternalServerError, gin.H{"error": "Failed to save info"})
|
||||
return
|
||||
}
|
||||
if rtenv.Cfg.GenDist {
|
||||
errModel := createRootModels(rtenv.Cfg)
|
||||
if errModel != nil {
|
||||
fmt.Printf("Error createRootModels: %v\n",errModel)
|
||||
}
|
||||
}
|
||||
logRoute(c,rtenv,"post_newuser",fmt.Sprintf("post %s: %s",cvpost.U,keys), fmt.Sprintf("post %s: %s",cvpost.U,keys))
|
||||
c.JSON(http.StatusOK, gin.H{"status": "ok"})
|
||||
// c.IndentedJSON(http.StatusCreated, cvdata)
|
||||
}
|
||||
|
183
jwt.go
Normal file
183
jwt.go
Normal file
@ -0,0 +1,183 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
b64 "encoding/base64"
|
||||
"io/ioutil"
|
||||
"time"
|
||||
|
||||
jwt "github.com/appleboy/gin-jwt/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
gojwt "github.com/golang-jwt/jwt/v4"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// func makeTokenString(SigningAlgorithm string, username string, cfg *Config) string {
|
||||
// if SigningAlgorithm == "" {
|
||||
// SigningAlgorithm = "HS256"
|
||||
// }
|
||||
// token := jwt.New(jwt.GetSigningMethod(SigningAlgorithm))
|
||||
// claims := token.Claims.(jwt.MapClaims)
|
||||
// claims["identity"] = username
|
||||
// claims["exp"] = time.Now().Add(time.Hour).Unix()
|
||||
// claims["orig_iat"] = time.Now().Unix()
|
||||
// var tokenString string
|
||||
// if SigningAlgorithm == "RS256" {
|
||||
// keyData, _ := ioutil.ReadFile(cfg.KeyPem)
|
||||
// signKey, _ := jwt.ParseRSAPrivateKeyFromPEM(keyData)
|
||||
// tokenString, _ = token.SignedString(signKey)
|
||||
// } else {
|
||||
// tokenString, _ = token.SignedString(key)
|
||||
// }
|
||||
// return tokenString
|
||||
// }
|
||||
// func makeTokenString(mw *jwt.GinJWTMiddleware,data interface{}) (string,error) {
|
||||
func makeTokenString(rtenv *RouteEnv,data interface{}) (string,error) {
|
||||
if rtenv.AuthMiddleware == nil {
|
||||
return "", nil
|
||||
}
|
||||
token := gojwt.New(gojwt.GetSigningMethod(rtenv.AuthMiddleware.SigningAlgorithm))
|
||||
claims := token.Claims.(gojwt.MapClaims)
|
||||
if rtenv.AuthMiddleware.PayloadFunc != nil {
|
||||
for key, value := range rtenv.AuthMiddleware.PayloadFunc(data) {
|
||||
claims[key] = value
|
||||
}
|
||||
}
|
||||
expire := rtenv.AuthMiddleware.TimeFunc().Add(rtenv.AuthMiddleware.Timeout)
|
||||
claims["exp"] = expire.Unix()
|
||||
claims["orig_iat"] = rtenv.AuthMiddleware.TimeFunc().Unix()
|
||||
var tokenString string
|
||||
var err error
|
||||
if rtenv.Cfg.SigningAlgorithm == "RS256" {
|
||||
// if mw.usingPublicKeyAlgo() {
|
||||
keyData, _ := ioutil.ReadFile(rtenv.Cfg.JwtKeyPem)
|
||||
signKey, _ := gojwt.ParseRSAPrivateKeyFromPEM(keyData)
|
||||
tokenString, err = token.SignedString(signKey)
|
||||
} else {
|
||||
tokenString, err = token.SignedString(rtenv.AuthMiddleware.Key)
|
||||
}
|
||||
return tokenString, err
|
||||
}
|
||||
func getJwt(rtenv *RouteEnv)*jwt.GinJWTMiddleware {
|
||||
// the jwt middleware
|
||||
authMiddleware, err := jwt.New(&jwt.GinJWTMiddleware{
|
||||
Realm: rtenv.Cfg.JwtRealm,
|
||||
Key: []byte(rtenv.Cfg.JwtKey),
|
||||
Timeout: time.Duration(rtenv.Cfg.JwtTimeout) * time.Minute,
|
||||
MaxRefresh: time.Duration(rtenv.Cfg.JwtMaxRefresh) * time.Minute,
|
||||
IdentityKey: rtenv.Cfg.IdentityKey,
|
||||
PrivKeyFile: rtenv.Cfg.KeyPem,
|
||||
PubKeyFile: rtenv.Cfg.CertPem,
|
||||
PayloadFunc: func(data interface{}) jwt.MapClaims {
|
||||
if v, ok := data.(*User); ok {
|
||||
return jwt.MapClaims{
|
||||
rtenv.Cfg.IdentityKey: v.UserName,
|
||||
"uuid": v.UUID,
|
||||
"data": v.Data,
|
||||
}
|
||||
}
|
||||
return jwt.MapClaims{}
|
||||
},
|
||||
IdentityHandler: func(c *gin.Context) interface{} {
|
||||
claims := jwt.ExtractClaims(c)
|
||||
username := ""
|
||||
name, okname := claims[rtenv.Cfg.IdentityKey].(string)
|
||||
if (okname) {
|
||||
username = name
|
||||
}
|
||||
return &User{ UserName: username }
|
||||
},
|
||||
Authenticator: func(c *gin.Context) (interface{}, error) {
|
||||
var loginVals Login
|
||||
if err := c.ShouldBind(&loginVals); err != nil {
|
||||
return "", jwt.ErrMissingLoginValues
|
||||
}
|
||||
userID := loginVals.Username
|
||||
password := loginVals.Password
|
||||
if val, ok := rtenv.Users.Accounts[userID]; ok {
|
||||
pasw,_ := b64.StdEncoding.DecodeString(password)
|
||||
pass := string(pasw)
|
||||
txtdata,err := decrypt(val.Passwd, string(CRYPTKEY))
|
||||
if err == nil && txtdata == pass {
|
||||
return &User{
|
||||
UserName: val.Id,
|
||||
LastName: "",
|
||||
FirstName: "",
|
||||
}, nil
|
||||
}
|
||||
}
|
||||
return nil, jwt.ErrFailedAuthentication
|
||||
},
|
||||
Authorizator: func(data interface{}, c *gin.Context) bool {
|
||||
if v, ok := data.(*User); ok {
|
||||
if v.UserName == rtenv.Cfg.PubUser {
|
||||
return true
|
||||
}
|
||||
if _, ok := rtenv.Users.Accounts[v.UserName]; ok {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
},
|
||||
Unauthorized: func(c *gin.Context, code int, message string) {
|
||||
c.JSON(code, gin.H{
|
||||
"code": code,
|
||||
"message": message,
|
||||
})
|
||||
},
|
||||
// TokenLookup is a string in the form of "<source>:<name>" that is used
|
||||
// to extract token from the request.
|
||||
// Optional. Default value "header:Authorization".
|
||||
// Possible values:
|
||||
// - "header:<name>"
|
||||
// - "query:<name>"
|
||||
// - "cookie:<name>"
|
||||
// - "param:<name>"
|
||||
TokenLookup: "header: Authorization, query: token, cookie: jwt",
|
||||
// TokenLookup: "query:token",
|
||||
// TokenLookup: "cookie:token",
|
||||
|
||||
// TokenHeadName is a string in the header. Default value is "Bearer"
|
||||
TokenHeadName: "Bearer",
|
||||
// TimeFunc provides the current time. You can override it to use another time value. This is useful for testing or if your server uses a different time zone than your tokens.
|
||||
TimeFunc: time.Now,
|
||||
})
|
||||
if err != nil {
|
||||
log.Fatal("JWT Error:" + err.Error())
|
||||
}
|
||||
return authMiddleware
|
||||
}
|
||||
|
||||
// func refreshToken(c *gin.Context, rtenv *RouteEnv,data interface{}) (string, time.Time, error) {
|
||||
// var expire time.Time
|
||||
// claims, err := rtenv.AuthMiddleware.CheckIfTokenExpire(c)
|
||||
// if err != nil {
|
||||
// return "", expire, err
|
||||
// }
|
||||
// fmt.Printf("Refresh token: %v\n",claims)
|
||||
// var token string
|
||||
// token,err = makeTokenString(rtenv.AuthMiddleware,data)
|
||||
// expire = time.Now()
|
||||
|
||||
// // // set cookie
|
||||
// // if rtenv.AuthMiddleware.SendCookie {
|
||||
// // expireCookie := rtenv.AuthMiddleware.TimeFunc().Add(rtenv.AuthMiddleware.CookieMaxAge)
|
||||
// // maxage := int(expireCookie.Unix() - time.Now().Unix())
|
||||
|
||||
// // if rtenv.AuthMiddleware.CookieSameSite != 0 {
|
||||
// // c.SetSameSite(rtenv.AuthMiddleware.CookieSameSite)
|
||||
// // }
|
||||
|
||||
// // c.SetCookie(
|
||||
// // rtenv.AuthMiddleware.CookieName,
|
||||
// // tokenString,
|
||||
// // maxage,
|
||||
// // "/",
|
||||
// // rtenv.AuthMiddleware.CookieDomain,
|
||||
// // rtenv.AuthMiddleware.SecureCookie,
|
||||
// // rtenv.AuthMiddleware.CookieHTTPOnly,
|
||||
// // )
|
||||
// // }
|
||||
// return token, expire, nil
|
||||
// }
|
193
loads.go
Normal file
193
loads.go
Normal file
@ -0,0 +1,193 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
cfg "github.com/jesusperez/cfgsrv"
|
||||
utils "github.com/jesusperez/datautils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
"gopkg.in/yaml.v2"
|
||||
)
|
||||
func loadUsersData(usersPath string) (map[string]Account, error) {
|
||||
var usersData map[string]Account
|
||||
d,file,errpath := decoderFromFile(usersPath)
|
||||
if errpath != nil {
|
||||
return nil, errpath
|
||||
}
|
||||
defer file.Close()
|
||||
if err := d.Decode(&usersData); err != nil {
|
||||
log.Fatalf("Decode: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return usersData, nil
|
||||
}
|
||||
func loadUsersAccounts(usersPath string) (*UsersAccounts, error) {
|
||||
accounts,err := loadUsersData(usersPath)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
keyAccounts := accounts
|
||||
for key, user := range accounts {
|
||||
user.Id = key
|
||||
keyAccounts[key] = user
|
||||
if len(user.Email) > 0 {
|
||||
accounts[user.Email] = user
|
||||
}
|
||||
}
|
||||
users := &UsersAccounts{
|
||||
Accounts: accounts,
|
||||
}
|
||||
return users, nil
|
||||
}
|
||||
func loadWebAccounts(users *UsersAccounts) (*WebAccounts) {
|
||||
webusers := &WebAccounts{}
|
||||
for key, user := range users.Accounts {
|
||||
if user.Web {
|
||||
webusers.Accounts[key] = user.Passwd
|
||||
}
|
||||
}
|
||||
return webusers
|
||||
}
|
||||
func loadModelsUsers(modelsPath string) (map[string]ModelUser, error) {
|
||||
var mdlsUsers map[string]ModelUser
|
||||
d,file,errpath := decoderFromFile(modelsPath)
|
||||
if errpath != nil {
|
||||
return nil, errpath
|
||||
}
|
||||
defer file.Close()
|
||||
if err := d.Decode(&mdlsUsers); err != nil {
|
||||
log.Fatalf("Decode: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
// fmt.Printf("users: %+v\n", users)
|
||||
return mdlsUsers, nil
|
||||
}
|
||||
func checkUserInvitation(key string,invitation Invitation, num int) (Invitation,bool,bool) {
|
||||
userInvitation := invitation
|
||||
needSync := false
|
||||
valid := true
|
||||
if invitation.Howmany > -1 && num > 0 {
|
||||
needSync = true
|
||||
userInvitation.Howmany = invitation.Howmany - num
|
||||
if userInvitation.Howmany < 0 {
|
||||
userInvitation.Howmany = 0
|
||||
userInvitation.Active = false
|
||||
valid = false
|
||||
}
|
||||
}
|
||||
if len(invitation.Expire) > 0 {
|
||||
res,err := time.Parse(time.RFC3339,invitation.Expire)
|
||||
if err == nil {
|
||||
if res.Before(time.Now()) {
|
||||
needSync = true
|
||||
userInvitation.Active = false
|
||||
valid = false
|
||||
}
|
||||
} else {
|
||||
fmt.Printf("Error parse invitation %s expire %s: %v",key,invitation.Expire,err)
|
||||
}
|
||||
}
|
||||
return userInvitation,valid,needSync
|
||||
}
|
||||
func checkUsersInvitations(invitations map[string]Invitation,writeSync bool,invitationsPath string) map[string]Invitation {
|
||||
usersInvitations := map[string]Invitation{}
|
||||
savedInvitations := invitations
|
||||
needSync := false
|
||||
for key, item := range invitations {
|
||||
if item.Active {
|
||||
invitation,valid,itemSync := checkUserInvitation(key,item,0)
|
||||
if (itemSync && !needSync) {
|
||||
needSync = true
|
||||
savedInvitations[key] = invitation
|
||||
}
|
||||
if (valid) {
|
||||
usersInvitations[key] = invitation
|
||||
}
|
||||
}
|
||||
}
|
||||
if (writeSync && needSync) {
|
||||
strdata, yamlerr := yaml.Marshal(savedInvitations)
|
||||
if yamlerr != nil {
|
||||
fmt.Printf("Error parse invitations: %v",yamlerr)
|
||||
} else {
|
||||
if res := utils.WriteData(string(strdata),invitationsPath) ; res != nil {
|
||||
fmt.Printf("Error write invitations: %v",res)
|
||||
}
|
||||
}
|
||||
}
|
||||
return usersInvitations
|
||||
}
|
||||
func loadUsersInvitations(invitationsPath string) (map[string]Invitation, error) {
|
||||
var invitations map[string]Invitation
|
||||
d,file,errpath := decoderFromFile(invitationsPath)
|
||||
if errpath != nil {
|
||||
return nil, errpath
|
||||
}
|
||||
defer file.Close()
|
||||
if err := d.Decode(&invitations); err != nil {
|
||||
log.Fatalf("Decode: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
return invitations, nil
|
||||
}
|
||||
func getUsersInvitations(invitationsPath string,writeSync bool) (map[string]Invitation, error) {
|
||||
invitations,err := loadUsersInvitations(invitationsPath)
|
||||
if err != nil {
|
||||
fmt.Printf("Error load invitations: %v",err)
|
||||
return nil,err
|
||||
}
|
||||
return checkUsersInvitations(invitations,writeSync,invitationsPath), nil
|
||||
}
|
||||
func loadConfig(path string) (*cfg.Config, error) {
|
||||
config := &cfg.Config{}
|
||||
// fmt.Printf("Loaded config: %#v\n", path)
|
||||
// if yamlFile, err := ioutil.ReadFile(path); err == nil {
|
||||
// err = yaml.Unmarshal(yamlFile, config)
|
||||
// if err != nil {
|
||||
// log.Fatalf("Unmarshal: %v", err)
|
||||
// }
|
||||
// }
|
||||
// var data interface{} = config
|
||||
d,file,errpath := decoderFromFile(path)
|
||||
if errpath != nil {
|
||||
return nil, errpath
|
||||
}
|
||||
defer file.Close()
|
||||
if err := d.Decode(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(config.MailPswd) > 0 {
|
||||
txtdata,err := decrypt(config.MailPswd, string(CRYPTKEY))
|
||||
if err == nil {
|
||||
config.MailPswd = txtdata
|
||||
}
|
||||
}
|
||||
return config, nil
|
||||
}
|
||||
func newDataConfig(runFlags RunFlags) (*cfg.Config, error) {
|
||||
config,err := loadConfig(runFlags.cfgPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Decode: %v", err)
|
||||
return nil, err
|
||||
}
|
||||
DEBUGLEVEL = config.DebugLevel
|
||||
return config, nil
|
||||
}
|
||||
|
||||
func loadDataConfig(runFlags RunFlags) (*cfg.Config, error) {
|
||||
cfg, err := newDataConfig(runFlags)
|
||||
if err != nil {
|
||||
log.Fatalf("Load data config: %v", err)
|
||||
return cfg, err
|
||||
}
|
||||
return cfg, nil
|
||||
}
|
||||
|
||||
func loadcertkey(runFlags RunFlags) {
|
||||
var err error
|
||||
CRYPTKEY, err = loadPath(runFlags.keyPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Error load key: %v", err)
|
||||
}
|
||||
}
|
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 14 KiB |
File diff suppressed because one or more lines are too long
Before Width: | Height: | Size: 14 KiB |
124
logs.go
Normal file
124
logs.go
Normal file
@ -0,0 +1,124 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"net"
|
||||
"net/http"
|
||||
"os"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
cfg "github.com/jesusperez/cfgsrv"
|
||||
utils "github.com/jesusperez/datautils"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
func setReqLog(cfg *cfg.Config) {
|
||||
todayLog := fmt.Sprintf("%s/%s.json",cfg.RequestOut,time.Now().Format("2006_01_02"))
|
||||
if todayLog == PathReqLog {
|
||||
return
|
||||
} else {
|
||||
PathReqLog = todayLog
|
||||
}
|
||||
err := utils.CheckDirPath(fmt.Sprintf("%s",cfg.RequestOut))
|
||||
if err != nil {
|
||||
log.Fatalf("setReqLog path %s: %v",cfg.RequestOut,err)
|
||||
}
|
||||
reqOut, err := os.OpenFile(PathReqLog, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
log.Fatalf("setReqLog: %v", err)
|
||||
}
|
||||
ReqLog = log.New()
|
||||
ReqLog.SetOutput(reqOut)
|
||||
ReqLog.SetFormatter(&log.JSONFormatter{
|
||||
PrettyPrint: false,
|
||||
})
|
||||
}
|
||||
func setLog(cfg *cfg.Config) {
|
||||
environment := os.Getenv("ENVIRONMENT")
|
||||
if environment == "production" {
|
||||
log.SetFormatter(&log.JSONFormatter{})
|
||||
file, err := os.OpenFile(cfg.LogOut, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0666)
|
||||
if err != nil {
|
||||
log.Fatalf("Open LogOut: %v", err)
|
||||
}
|
||||
log.SetOutput(file)
|
||||
// Only log the warning severity or above.
|
||||
log.SetLevel(log.WarnLevel)
|
||||
} else {
|
||||
// The TextFormatter is default, you don't actually have to do this.
|
||||
log.SetFormatter(&log.TextFormatter{})
|
||||
log.SetOutput(os.Stdout)
|
||||
}
|
||||
log.SetFormatter(&log.TextFormatter{
|
||||
DisableColors: false,
|
||||
FullTimestamp: true,
|
||||
})
|
||||
}
|
||||
|
||||
func getIP(r *http.Request) (string, error) {
|
||||
//Get IP from the X-REAL-IP header
|
||||
ip := r.Header.Get("X-REAL-IP")
|
||||
netIP := net.ParseIP(ip)
|
||||
if netIP != nil {
|
||||
return ip, nil
|
||||
}
|
||||
//Get IP from X-FORWARDED-FOR header
|
||||
ips := r.Header.Get("X-FORWARDED-FOR")
|
||||
splitIps := strings.Split(ips, ",")
|
||||
for _, ip := range splitIps {
|
||||
netIP := net.ParseIP(ip)
|
||||
if netIP != nil {
|
||||
return ip, nil
|
||||
}
|
||||
}
|
||||
//Get IP from RemoteAddr
|
||||
ip, _, err := net.SplitHostPort(r.RemoteAddr)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
netIP = net.ParseIP(ip)
|
||||
if netIP != nil {
|
||||
return ip, nil
|
||||
}
|
||||
return "", fmt.Errorf("No valid ip found")
|
||||
}
|
||||
|
||||
func logRequest(c *gin.Context, rtenv *RouteEnv, run string, route string, info string, infoReq string, tkn string) {
|
||||
accept_langs := strings.Split(c.Request.Header.Get("Accept-Language"),";")
|
||||
langs := strings.Split(accept_langs[0], ",")
|
||||
ip, _ := getIP(c.Request)
|
||||
// header, _ := ioutil.ReadAll(c.Request.Header)
|
||||
//println(string(c.header))
|
||||
// println(c.Header)
|
||||
// ipp := c.Request.RemoteAddr
|
||||
// xforward := c.Request.Header.Get("X-Forwarded-For")
|
||||
// fm.Println("IP : ", ipp)
|
||||
// fmt.Println("X-Forwarded-For : ", xforward)
|
||||
// fmt.Println("IP : ", ip)
|
||||
// // if err != nil {
|
||||
// dt := time.Now()
|
||||
// fmt.Println("Current date and time is: ", dt.String())
|
||||
// fmt.Println(dt.Format("01-02-2006 15:04:05"))
|
||||
// fmt.Println(dt.Format(time.RFC3339Nano))
|
||||
if len(info) > 0 {
|
||||
setReqLog(rtenv.Cfg)
|
||||
ReqLog.WithFields(log.Fields{
|
||||
"run": run,
|
||||
"route": route,
|
||||
"lang": fmt.Sprintf("%+v",langs),
|
||||
"agent": fmt.Sprintf("%+v",c.Request.UserAgent()),
|
||||
"ip": ip,
|
||||
"tkn": tkn,
|
||||
}).Info(info)
|
||||
}
|
||||
if len(infoReq) > 0 {
|
||||
log.WithFields(log.Fields{
|
||||
"run": "route",
|
||||
"route": "/",
|
||||
"ip": ip,
|
||||
"tkn": tkn,
|
||||
}).Info(infoReq)
|
||||
}
|
||||
}
|
138
main.go
Normal file
138
main.go
Normal file
@ -0,0 +1,138 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"flag"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/google/uuid"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
const VERSION = "0.0.1"
|
||||
const SEPARATOR = "::"
|
||||
const CFGPATH = "config.yaml"
|
||||
const GENPATH = "dist"
|
||||
const DFLTCOMMAND = "webservice"
|
||||
const KEYPATH = ".k"
|
||||
|
||||
var CRYPTKEY []byte
|
||||
var DEBUGLEVEL int
|
||||
var ReqLog *log.Logger
|
||||
var PathReqLog string
|
||||
|
||||
var f embed.FS
|
||||
var appDB = make(map[string]AppData)
|
||||
|
||||
func ParseFlags() (RunFlags,error) {
|
||||
runFlags := RunFlags {
|
||||
command: os.Getenv("CVGEN_COMMAND"),
|
||||
cfgPath: os.Getenv("CVGEN_CFGPATH"),
|
||||
genPath: os.Getenv("CVGEN_GENPATH"),
|
||||
keyPath: os.Getenv("CVGEN_KEYPATH"),
|
||||
}
|
||||
if runFlags.command == "" {
|
||||
runFlags.command = DFLTCOMMAND
|
||||
}
|
||||
if runFlags.cfgPath == "" {
|
||||
runFlags.cfgPath = CFGPATH
|
||||
}
|
||||
if runFlags.keyPath == "" {
|
||||
runFlags.keyPath = KEYPATH
|
||||
}
|
||||
if runFlags.genPath == "" {
|
||||
runFlags.genPath = GENPATH
|
||||
}
|
||||
commandInfo := fmt.Sprintf("command to run [\n%s]",
|
||||
"uuid, huuid, genkey, encrypt, decrypt, gendata, webservice")
|
||||
flag.StringVar(&runFlags.command, "c", runFlags.command, commandInfo)
|
||||
flag.StringVar(&runFlags.cfgPath, "f", runFlags.cfgPath, "path to config file")
|
||||
flag.StringVar(&runFlags.modelName, "m", runFlags.modelName, "[gendata] model-name to generate json file")
|
||||
flag.StringVar(&runFlags.genPath, "g", runFlags.genPath, "[gendata] path to generate model-name.json file")
|
||||
flag.StringVar(&runFlags.keyPath, "k", runFlags.keyPath, "path to key file")
|
||||
flag.StringVar(&runFlags.text, "t", runFlags.text, "[crypt] text to encrypt/decrypt")
|
||||
flag.StringVar(&runFlags.url, "u", runFlags.url, "[webservice] url to open in browser")
|
||||
flag.StringVar(&runFlags.textPath, "p", runFlags.textPath, "[crypt] text path to encrypt/decrypt")
|
||||
flag.BoolVar(&runFlags.version,"v",false,"version")
|
||||
flag.Parse()
|
||||
return runFlags,nil
|
||||
}
|
||||
|
||||
func run() int {
|
||||
runFlags, err := ParseFlags()
|
||||
if err != nil {
|
||||
log.Fatalf("Run flags parse: %v", err)
|
||||
}
|
||||
switch runFlags.command {
|
||||
case "genkey":
|
||||
ky, err := loadPath(runFlags.keyPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Error load key: %v", err)
|
||||
}
|
||||
randKey, err := getKey(ky)
|
||||
if err != nil {
|
||||
log.Fatalf("Error gen key: %v", err)
|
||||
}
|
||||
fmt.Printf("%s\n", randKey)
|
||||
return 0
|
||||
case "encrypt":
|
||||
var data string
|
||||
loadcertkey(runFlags)
|
||||
if len(runFlags.text) > 0 {
|
||||
data,err = encrypt(runFlags.text, string(CRYPTKEY))
|
||||
} else if len(runFlags.textPath) > 0 {
|
||||
fileData, err := loadPath(runFlags.textPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Error encrypt: %v", err)
|
||||
}
|
||||
data,err = encrypt(string(fileData), string(CRYPTKEY))
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
return 0
|
||||
case "decrypt":
|
||||
var data string
|
||||
loadcertkey(runFlags)
|
||||
if len(runFlags.text) > 0 {
|
||||
data,err = decrypt(runFlags.text, string(CRYPTKEY))
|
||||
} else if len(runFlags.textPath) > 0 {
|
||||
fileData, err := loadPath(runFlags.textPath)
|
||||
if err != nil {
|
||||
log.Fatalf("Error decrypt: %v", err)
|
||||
}
|
||||
data,err = decrypt(string(fileData), string(CRYPTKEY))
|
||||
}
|
||||
fmt.Printf("%s\n", data)
|
||||
return 0
|
||||
case "gendata":
|
||||
return gendata(runFlags)
|
||||
case "webservice":
|
||||
loadcertkey(runFlags)
|
||||
return webservice(runFlags)
|
||||
case "huuid":
|
||||
uuid := uuid.NewString()
|
||||
fmt.Printf("%s\n", uuid)
|
||||
return 0
|
||||
case "uuid":
|
||||
uuid := uuid.NewString()
|
||||
fmt.Printf("%s\n", strings.Replace(uuid,"-","",-1))
|
||||
return 0
|
||||
default:
|
||||
fmt.Fprintln(os.Stderr, "Unknown command: ", runFlags.command)
|
||||
return 99
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
if len(os.Args) > 1 && (os.Args[1] == "-v" || os.Args[1] == "version") {
|
||||
fmt.Fprintf(os.Stdout, "ServCVgen version:%s\n",VERSION)
|
||||
os.Exit(0)
|
||||
}
|
||||
// if len(os.Args) > 1 || len(os.Getenv("CVGEN_COMMAND")) > 0 {
|
||||
// os.Exit(run())
|
||||
// } else {
|
||||
// fmt.Fprintln(os.Stderr, "Use --help to see options")
|
||||
// }
|
||||
os.Exit(run())
|
||||
}
|
155
models.go
Normal file
155
models.go
Normal file
@ -0,0 +1,155 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"os"
|
||||
|
||||
"gopkg.in/yaml.v2"
|
||||
|
||||
cfg "github.com/jesusperez/cfgsrv"
|
||||
cvdata "github.com/jesusperez/cvdata"
|
||||
utils "github.com/jesusperez/datautils"
|
||||
)
|
||||
|
||||
func createRootModels(config *cfg.Config) error {
|
||||
modelsFullPath := fmt.Sprintf("%s/%s",config.DataPath,config.DataModelsRoot)
|
||||
var models []cvdata.ModelType
|
||||
err := filepath.Walk(config.DataPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
if _, err := os.Stat(fmt.Sprintf("%s/core.yaml",path)); err == nil {
|
||||
if !strings.Contains(path,"lang") {
|
||||
name := filepath.Base(path)
|
||||
modelPath := strings.Replace(path,config.DataPath,"",-1)
|
||||
models = append(models, cvdata.ModelType{
|
||||
Id: name,
|
||||
Title: name,
|
||||
Path: fmt.Sprintf("~/%s",modelPath),
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Error create %s: %v\n",modelsFullPath, err)
|
||||
return err
|
||||
}
|
||||
var rootModels cvdata.Models
|
||||
rootModels.Models = models
|
||||
data, err := yaml.Marshal(rootModels)
|
||||
if err != nil {
|
||||
fmt.Printf("Error create yaml models data: %v\n", err)
|
||||
return err
|
||||
}
|
||||
dataModelsPath := fmt.Sprintf("%s/%s",config.DataPath,config.DataModelsRoot)
|
||||
if res := utils.WriteData(string(data),dataModelsPath) ; res != nil {
|
||||
fmt.Printf("Error write %s: %v\n", dataModelsPath, err)
|
||||
return res
|
||||
}
|
||||
data, err = json.Marshal(rootModels)
|
||||
if err != nil {
|
||||
fmt.Printf("Error create json models data: %v\n", err)
|
||||
return err
|
||||
}
|
||||
dataModelsPath = fmt.Sprintf("%s/%s",config.DataDistPath,strings.Replace(config.DataModelsRoot,".yaml",".json",-1))
|
||||
if res := utils.WriteData(string(data),dataModelsPath) ; res != nil {
|
||||
fmt.Printf("Error write %s: %v\n", dataModelsPath, err)
|
||||
return res
|
||||
}
|
||||
fmt.Printf("Root models write from %s\n", modelsFullPath)
|
||||
return nil
|
||||
}
|
||||
func genJsonModels(config *cfg.Config) error {
|
||||
if config.UseRepo && config.RepoPath != "" {
|
||||
var errgit error
|
||||
var cmd *exec.Cmd
|
||||
if cmd,errgit = utils.GitPull(config.RepoPath,config.RepoName,config.BackgGit,config.QuietGit); errgit != nil {
|
||||
fmt.Printf("Error pull %s (%s): %v\n", config.RepoPath, config.RepoName,errgit)
|
||||
if config.BackgGit {
|
||||
if errgit = cmd.Wait(); errgit != nil {
|
||||
fmt.Printf("Error pull %s (%s): %v\n", config.RepoPath, config.RepoName,errgit)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
err := filepath.Walk(config.DataPath, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return err
|
||||
}
|
||||
if info.IsDir() {
|
||||
if _, err := os.Stat(fmt.Sprintf("%s/core.yaml",path)); err == nil {
|
||||
if !strings.Contains(path,"lang") {
|
||||
dir := filepath.Base(path)
|
||||
exclude := false
|
||||
for _,itm := range config.GenExcludeList {
|
||||
if itm == dir {
|
||||
exclude = true
|
||||
break
|
||||
}
|
||||
}
|
||||
if !exclude {
|
||||
if errjson := makeModelJson(config, dir, config.DataDistPath, true, false); errjson != nil {
|
||||
fmt.Printf("Error write cv %s: %v\n", dir, errjson)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
})
|
||||
return err
|
||||
}
|
||||
func makeModelJson(cfg *cfg.Config, modelName string, genPath string, quiet bool, useRepo bool) error {
|
||||
cvdata, error := cvdata.LoadCVData(fmt.Sprintf("%s/%s",cfg.DataPath,modelName), cfg, useRepo)
|
||||
if error != nil {
|
||||
fmt.Printf("Error generating data %v\n", error)
|
||||
}
|
||||
data, err := json.Marshal(cvdata)
|
||||
if err != nil {
|
||||
fmt.Printf("Error Parse cv %s: %v\n", modelName, err)
|
||||
return err
|
||||
} else {
|
||||
err := utils.CheckDirPath(fmt.Sprintf("%s",genPath))
|
||||
if err == nil {
|
||||
path := fmt.Sprintf("%s/%s.json",genPath,modelName)
|
||||
if res := utils.WriteData(string(data),path) ; res != nil {
|
||||
fmt.Printf("Error write cv %s: %v\n", modelName, err)
|
||||
return err
|
||||
} else {
|
||||
if quiet {
|
||||
fmt.Printf("CV %s writed to: %s\n", modelName,path)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func gendata(runFlags RunFlags) int {
|
||||
cfg,err := loadDataConfig(runFlags)
|
||||
//fmt.Printf("Loaded config: %#v\n", cfg)
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
errModel := createRootModels(cfg)
|
||||
if errModel != nil {
|
||||
return 1
|
||||
}
|
||||
errModel = makeModelJson(cfg, runFlags.modelName, runFlags.genPath,true,true)
|
||||
if errModel != nil {
|
||||
return 1
|
||||
}
|
||||
return 0
|
||||
}
|
41
redis.go
Normal file
41
redis.go
Normal file
@ -0,0 +1,41 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/go-redis/redis/v8"
|
||||
cfg "github.com/jesusperez/cfgsrv"
|
||||
)
|
||||
|
||||
func load_redis(cfg *cfg.Config) *redis.Client {
|
||||
|
||||
rdb := redis.NewClient(&redis.Options{
|
||||
Addr: fmt.Sprintf("%s:%d",cfg.RedisHost,cfg.RedisPort),
|
||||
Password: cfg.RedisPswd, // no password set
|
||||
DB: 0, // TODO cfg.RedisDB name to number, // 0, use default DB
|
||||
})
|
||||
return rdb
|
||||
}
|
||||
//}
|
||||
//func load_redis(rtenv *RouteEnv) {
|
||||
// err := rdb.Set(ctx, "key", "value", 0).Err()
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
|
||||
// val, err := rdb.Get(ctx, "key").Result()
|
||||
// if err != nil {
|
||||
// panic(err)
|
||||
// }
|
||||
// fmt.Println("key", val)
|
||||
|
||||
// val2, err := rdb.Get(ctx, "key2").Result()
|
||||
// if err == redis.Nil {
|
||||
// fmt.Println("key2 does not exist")
|
||||
// } else if err != nil {
|
||||
// panic(err)
|
||||
// } else {
|
||||
// fmt.Println("key2", val2)
|
||||
// }
|
||||
//}
|
||||
|
189
routes.go
Normal file
189
routes.go
Normal file
@ -0,0 +1,189 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
jwt "github.com/appleboy/gin-jwt/v2"
|
||||
"github.com/casbin/casbin/v2"
|
||||
"github.com/gin-gonic/gin"
|
||||
cfg "github.com/jesusperez/cfgsrv"
|
||||
)
|
||||
|
||||
type RouteEnv struct {
|
||||
Cfg *cfg.Config
|
||||
Users *UsersAccounts
|
||||
WebUsrs *WebAccounts
|
||||
MdlsUsrs map[string]ModelUser
|
||||
Enforcer *casbin.Enforcer
|
||||
AuthMiddleware *jwt.GinJWTMiddleware
|
||||
Store *interface{}
|
||||
RunFlags *RunFlags
|
||||
}
|
||||
func logRoute(c *gin.Context, rtenv *RouteEnv, ky string, info string, infoReq string) {
|
||||
tkn := ""
|
||||
if rtenv.Cfg.UseJWT {
|
||||
tk, errtk := rtenv.AuthMiddleware.ParseToken(c)
|
||||
if errtk == nil {
|
||||
tkn = fmt.Sprintf("%v",tk.Raw)
|
||||
}
|
||||
}
|
||||
logRequest(c, rtenv, "route", rtenv.Cfg.Routes[ky].Path, info, infoReq, tkn)
|
||||
}
|
||||
func getRoutes(router *gin.Engine, cfg *cfg.Config, users *UsersAccounts, mdlsUsrs map[string]ModelUser, enforcer *casbin.Enforcer, store *interface{}, runFlags *RunFlags) {
|
||||
var authorized *gin.RouterGroup
|
||||
routenv := &RouteEnv{
|
||||
Cfg: cfg,
|
||||
Users: users,
|
||||
MdlsUsrs: mdlsUsrs,
|
||||
Enforcer: enforcer,
|
||||
AuthMiddleware: nil,
|
||||
Store: store,
|
||||
RunFlags: runFlags,
|
||||
}
|
||||
if cfg.UseJWT {
|
||||
routenv.AuthMiddleware = getJwt(routenv)
|
||||
router.POST(cfg.Routes["post_login"].Path, routenv.AuthMiddleware.LoginHandler)
|
||||
authorized = router.Group(cfg.RootAuthGroup)
|
||||
// authorized.GET(cfg.Routes["refreshauth"].Path,routenv.AuthMiddleware.RefreshHandler)
|
||||
authorized.GET(cfg.Routes["refreshauth"].Path, func(c *gin.Context) {
|
||||
get_auth_refresh_handle(c, routenv)
|
||||
})
|
||||
authorized.Use(routenv.AuthMiddleware.MiddlewareFunc())
|
||||
} else {
|
||||
webusrs := loadWebAccounts(users)
|
||||
// router.POST(cfg.Routes["post_login"].Path, routenv.AuthMiddleware.LoginHandler)
|
||||
authorized = router.Group(cfg.Routes["root"].Path, gin.BasicAuth(webusrs.Accounts))
|
||||
}
|
||||
authorized.GET("/hello", func(c *gin.Context) {
|
||||
if cfg.UseJWT {
|
||||
claims := jwt.ExtractClaims(c)
|
||||
user, _ := c.Get(cfg.IdentityKey)
|
||||
c.JSON(200, gin.H{
|
||||
"userID": claims[cfg.IdentityKey],
|
||||
"userName": user.(*User).UserName,
|
||||
"text": "Hello World.",
|
||||
})
|
||||
} else {
|
||||
c.JSON(200, gin.H{
|
||||
"userID": "",
|
||||
"userName": "",
|
||||
"text": "Hello World.",
|
||||
})
|
||||
}
|
||||
})
|
||||
// authorized.GET("/page/:id", func(c *gin.Context) {
|
||||
// logRoute := func(info string, infoReq string) {
|
||||
// logRequest(c, cfg, "route", "/", info, infoReq)
|
||||
// }
|
||||
// claims := jwt.ExtractClaims(c)
|
||||
// user, _ := c.Get(identityKey)
|
||||
// id := c.Params.ByName("id")
|
||||
// logRoute(fmt.Sprintf("get /"), fmt.Sprintf("get /"))
|
||||
// c.HTML(http.StatusOK, "welcome", gin.H{
|
||||
// "title": fmt.Sprintf("Main website %s for %s (%s)",id,user.(*User).UserName,claims[identityKey]),
|
||||
// })
|
||||
// })
|
||||
router.GET(cfg.Routes["root"].Path, func(c *gin.Context) {
|
||||
logRoute(c,routenv,"root",fmt.Sprintf("get %s",cfg.Routes["root"].Path), fmt.Sprintf("get %s",cfg.Routes["root"].Path))
|
||||
c.HTML(http.StatusOK, "welcome", gin.H{
|
||||
"title": "Main website public",
|
||||
})
|
||||
})
|
||||
router.GET(cfg.Routes["page"].Path, func(c *gin.Context) {
|
||||
get_page_handle(c, routenv )
|
||||
})
|
||||
router.GET(cfg.Routes["auth"].Path, func(c *gin.Context) {
|
||||
get_auth_handle(c, routenv )
|
||||
})
|
||||
router.GET(cfg.Routes["invitation"].Path, func(c *gin.Context) {
|
||||
get_invitation_handle(c, routenv )
|
||||
})
|
||||
authorized.POST(cfg.Routes["sendinvitation"].Path, func(c *gin.Context) {
|
||||
send_invitation_handle(c, routenv )
|
||||
})
|
||||
authorized.GET(cfg.Routes["users"].Path, func(c *gin.Context) {
|
||||
get_users_handle(c, routenv )
|
||||
})
|
||||
authorized.POST(cfg.Routes["post_users"].Path, func(c *gin.Context) {
|
||||
post_users_handle(c, routenv )
|
||||
})
|
||||
authorized.POST(cfg.Routes["post_newuser"].Path, func(c *gin.Context) {
|
||||
post_newuser_handle(c, routenv )
|
||||
})
|
||||
router.GET(cfg.Routes["recoveryaccess"].Path, func(c *gin.Context) {
|
||||
get_recoveryaccess_handle(c, routenv )
|
||||
})
|
||||
router.POST(cfg.Routes["post_recoveryaccess"].Path, func(c *gin.Context) {
|
||||
post_recoveryaccess_handle(c, routenv )
|
||||
|
||||
})
|
||||
router.GET("/user/:name", func(c *gin.Context) {
|
||||
user := c.Params.ByName("name")
|
||||
value, ok := users.Accounts[user]
|
||||
role := cfg.AdminRole
|
||||
if ok {
|
||||
roles,_ := enforcer.GetUsersForRole(role)
|
||||
hasRole := false
|
||||
c.JSON(http.StatusOK, gin.H{"user": user, "value": value, "roles": roles, "hasrole": hasRole})
|
||||
} else {
|
||||
c.HTML(http.StatusOK, "index", gin.H{
|
||||
"title": "Main website",
|
||||
})
|
||||
}
|
||||
})
|
||||
// authorized.GET(cfg.Routes["data"].Path, func(c *gin.Context) {
|
||||
router.GET(cfg.Routes["data"].Path, func(c *gin.Context) {
|
||||
get_data_handle(c, routenv )
|
||||
})
|
||||
for ky, tpl := range cfg.TemplatesFiles {
|
||||
if ky != "index" {
|
||||
fmt.Printf("%s -> %v\n",ky,tpl)
|
||||
kyval := fmt.Sprintf("%s",ky)
|
||||
router.GET(tpl.Route, func(c *gin.Context) {
|
||||
c.HTML(http.StatusOK, kyval, gin.H{
|
||||
"title": fmt.Sprintf("%s website",kyval),
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
authorized.POST(cfg.Routes["post_data"].Path, func(c *gin.Context) {
|
||||
post_data_handle(c, routenv )
|
||||
})
|
||||
authorized.GET(cfg.Routes["trackinglist"].Path, func(c *gin.Context) {
|
||||
get_tracking_list_handle(c, routenv )
|
||||
})
|
||||
authorized.GET(cfg.Routes["tracking"].Path, func(c *gin.Context) {
|
||||
get_tracking_handle(c, routenv )
|
||||
})
|
||||
authorized.POST(cfg.Routes["post_tracking"].Path, func(c *gin.Context) {
|
||||
post_tracking_handle(c, routenv )
|
||||
})
|
||||
authorized.GET(cfg.Routes["config"].Path, func(c *gin.Context) {
|
||||
get_config_handle(c, routenv )
|
||||
})
|
||||
authorized.POST(cfg.Routes["post_config"].Path, func(c *gin.Context) {
|
||||
post_config_handle(c, routenv )
|
||||
})
|
||||
router.GET("favicon.ico", func(c *gin.Context) {
|
||||
file, _ := f.ReadFile(fmt.Sprintf("%s/favicon.svg",cfg.AssetsPath))
|
||||
c.Data(
|
||||
http.StatusOK,
|
||||
"image/x-icon",
|
||||
file,
|
||||
)
|
||||
})
|
||||
if cfg.UseJWT {
|
||||
router.NoRoute(routenv.AuthMiddleware.MiddlewareFunc(), func(c *gin.Context) {
|
||||
claims := jwt.ExtractClaims(c)
|
||||
log.Printf("NoRoute claims: %#v\n", claims)
|
||||
c.JSON(404, gin.H{"code": "PAGE_NOT_FOUND", "message": "Page not found"})
|
||||
})
|
||||
} else {
|
||||
router.NoRoute(func(c *gin.Context) {
|
||||
// log.Printf("NoRoute claims: %#v\n", claims)
|
||||
c.JSON(404, gin.H{"code": "PAGE_NOT_FOUND", "message": "Page not found"})
|
||||
})
|
||||
}
|
||||
}
|
139
types.go
Normal file
139
types.go
Normal file
@ -0,0 +1,139 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
|
||||
"github.com/gin-gonic/gin"
|
||||
cfg "github.com/jesusperez/cfgsrv"
|
||||
)
|
||||
|
||||
type RunFlags struct {
|
||||
command string
|
||||
cfgPath string
|
||||
usersPath string
|
||||
modelName string
|
||||
keyPath string
|
||||
genPath string
|
||||
text string
|
||||
textPath string
|
||||
url string
|
||||
version bool
|
||||
}
|
||||
|
||||
type ModelUser struct {
|
||||
Model string `yaml:"model"`
|
||||
User string `yaml:"user"`
|
||||
Active bool `yaml:"active"`
|
||||
Data string `yaml:"data"`
|
||||
}
|
||||
type Invitation struct {
|
||||
Email string `yaml:"email"`
|
||||
Createdby string `yaml:"createdby"`
|
||||
Expire string `yaml:"expire"`
|
||||
Howmany int `yaml:"howmany"`
|
||||
Role string `yaml:"role"`
|
||||
Description string `yaml:"description"`
|
||||
Data string `yaml:"data"`
|
||||
Active bool `yaml:"active"`
|
||||
}
|
||||
type PostInvitation struct {
|
||||
Id string
|
||||
Data Invitation
|
||||
}
|
||||
type Account struct {
|
||||
Username string `yaml:"username"`
|
||||
Passwd string `yaml:"passwd"`
|
||||
Email string `yaml:"email"`
|
||||
Description string `yaml:"description"`
|
||||
Data string `yaml:"data"`
|
||||
Web bool `yaml:"web"`
|
||||
Id string `yaml:"id"`
|
||||
}
|
||||
type UsersAccounts struct {
|
||||
Accounts map[string]Account `yaml:"accounts"`
|
||||
}
|
||||
type NewUserData struct {
|
||||
Username string `yaml:"username" json:"username"`
|
||||
Passwd string `yaml:"password" json:"password"`
|
||||
Email string `yaml:"email" json:"email"`
|
||||
Role string `yaml:"role" json:"role"`
|
||||
Data string `yaml:"data" json:"data"`
|
||||
}
|
||||
type WebAccounts struct {
|
||||
Accounts gin.Accounts `yaml:"accounts"`
|
||||
}
|
||||
type Login struct {
|
||||
Username string `form:"username" json:"username" binding:"required"`
|
||||
Password string `form:"password" json:"password" binding:"required"`
|
||||
}
|
||||
type User struct {
|
||||
UserName string
|
||||
UUID string
|
||||
Data string
|
||||
FirstName string
|
||||
LastName string
|
||||
}
|
||||
// type Data interface {
|
||||
// type Config, int8, int16, int32, int64
|
||||
// }
|
||||
|
||||
type ConfigPostData struct {
|
||||
Id string `yaml:"id" json:"id"`
|
||||
Val string `yaml:"val" json:"val"`
|
||||
Config cfg.Config `yaml:"config" json:"config"`
|
||||
}
|
||||
type TrackAction struct {
|
||||
When string `yaml:"when" json:"when"`
|
||||
Where string `yaml:"where" json:"where"`
|
||||
What string `yaml:"what" json:"what"`
|
||||
Context string `yaml:"context" json:"context"`
|
||||
Data string `yaml:"data" json:"data"`
|
||||
Auth string `yaml:"auth" json:"auth"`
|
||||
}
|
||||
type PostTrackAction struct {
|
||||
When string `yaml:"when" json:"when"`
|
||||
Where string `yaml:"where" json:"where"`
|
||||
What string `yaml:"what" json:"what"`
|
||||
Context string `yaml:"context" json:"context"`
|
||||
Data string `yaml:"data" json:"data"`
|
||||
}
|
||||
type UsersData struct {
|
||||
UsersData string `yaml:"usersData" json:"usersData"`
|
||||
ModelsData string `yaml:"modelsData" json:"modelsData"`
|
||||
Invitations string `yaml:"invitations" json:"invitations"`
|
||||
AuthzModel string `yaml:"authzModel" json:"authzModel"`
|
||||
AuthzPolicy string `yaml:"authzPolicy" json:"authzPolicy"`
|
||||
}
|
||||
type UsersDataPost struct {
|
||||
Id string `yaml:"id" json:"id"`
|
||||
Val string `yaml:"val" json:"val"`
|
||||
Data UsersData `yaml:"data" json:"data"`
|
||||
}
|
||||
type UsersRecoveryPost struct {
|
||||
Id string `yaml:"id" json:"id"`
|
||||
Val string `yaml:"val" json:"val"`
|
||||
}
|
||||
type UsersRecover struct {
|
||||
Id string `yaml:"id" json:"id"`
|
||||
Data Account `yaml:"data" json:"data"`
|
||||
}
|
||||
type SaveDataInfo struct {
|
||||
Title string
|
||||
Data string
|
||||
Id string
|
||||
Path string
|
||||
Prfx string
|
||||
}
|
||||
type Mail struct {
|
||||
Sender string
|
||||
To []string
|
||||
Cc []string
|
||||
Bcc []string
|
||||
Subject string
|
||||
Body bytes.Buffer
|
||||
// Headers textproto.MIMEHeader
|
||||
}
|
||||
type AppData struct {
|
||||
Data interface{}
|
||||
Expire string
|
||||
}
|
246
utils.go
Normal file
246
utils.go
Normal file
@ -0,0 +1,246 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
// "io/ioutil"
|
||||
// "path/filepath"
|
||||
// "time"
|
||||
|
||||
//"github.com/go-git/go-git/v5"
|
||||
"gopkg.in/yaml.v3"
|
||||
)
|
||||
|
||||
func loadPath(path string) ([]byte,error) {
|
||||
content, err := ioutil.ReadFile(path)
|
||||
if err != nil {
|
||||
return nil,err
|
||||
}
|
||||
return content, nil
|
||||
}
|
||||
func decoderFromFile(path string) (*yaml.Decoder, *os.File, error) {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
// log.Fatalf("open file error: %v", err)
|
||||
fmt.Printf("open file error: %v\n", err)
|
||||
return nil,nil, err
|
||||
}
|
||||
return yaml.NewDecoder(file), file, nil
|
||||
}
|
||||
func existsPathErr(path string) error {
|
||||
_, err := os.Stat(path)
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return err // errors.New("File not exists")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func existsPath(path string) bool {
|
||||
_, err := os.Stat(path)
|
||||
return !errors.Is(err, os.ErrNotExist)
|
||||
}
|
||||
func checkDirPath(path string) error {
|
||||
if ! existsPath(path) {
|
||||
os.Mkdir(path, os.FileMode(0755))
|
||||
if err := existsPathErr(path); err != nil {
|
||||
fmt.Printf("Error creating path %s: %v\n", path,err)
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
// https://github.com/go-git/go-git/blob/master/_examples/commit/main.go
|
||||
func writeData(data string, path string) error {
|
||||
//fmt.Printf("Write %s\n", path)
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Error create %s: %v\n", path, err)
|
||||
return err
|
||||
}
|
||||
w := bufio.NewWriter(f)
|
||||
n4, err := w.WriteString(data)
|
||||
if err != nil {
|
||||
fmt.Printf("Error writing %s: %v\n", path, err)
|
||||
return err
|
||||
}
|
||||
if DEBUGLEVEL > 0 {
|
||||
fmt.Printf("%s wrote %d bytes\n", path, n4)
|
||||
}
|
||||
w.Flush()
|
||||
return nil
|
||||
}
|
||||
|
||||
func execCommand(command string, args []string,quiet bool) (string,error) {
|
||||
cmd := exec.Command(command, args...)
|
||||
cmd.Stdin = strings.NewReader("")
|
||||
var out bytes.Buffer
|
||||
cmd.Stdout = &out
|
||||
err := cmd.Run()
|
||||
if err != nil {
|
||||
if !quiet {
|
||||
fmt.Printf("Error exec command %s: %#v\n", cmd,err)
|
||||
}
|
||||
return out.String(),err
|
||||
}
|
||||
if !quiet {
|
||||
fmt.Printf("%q\n", out.String())
|
||||
}
|
||||
return out.String(),nil
|
||||
}
|
||||
func gitPull(path string, name string,quiet bool) error {
|
||||
fmt.Printf("Git Pull: %#v\n", path)
|
||||
_, err := execCommand("git",[]string{"-C",path,"pull",name},quiet)
|
||||
return err
|
||||
}
|
||||
func gitCommit(path string, file string, msg string, target string, quiet bool) error {
|
||||
fmt.Printf("Git Commit: %#v\n", path)
|
||||
if out,err := execCommand("git",[]string{"-C",path,"add",file},quiet); err != nil {
|
||||
fmt.Printf("Error git add %s: %s\n %#v\n", path, out,err)
|
||||
//return err
|
||||
}
|
||||
if _,err := execCommand("git",[]string{"-C",path,"commit","-m",msg},quiet); err != nil {
|
||||
//if len(out) != 0 {
|
||||
//fmt.Printf("Error commit %s: %s\n %#v\n", path, out,err)
|
||||
// return err
|
||||
// }
|
||||
return nil
|
||||
}
|
||||
// if out,err := execCommand("git",[]string{"-C",path,"diff","--stat","--cached",target},quiet); err != nil {
|
||||
// if len(out) == 0 {
|
||||
// return nil
|
||||
// } else {
|
||||
// fmt.Printf("Error commit %s: %s\n %#v\n", path, out,err)
|
||||
// return err
|
||||
// }
|
||||
// }
|
||||
if _,err := execCommand("git",[]string{"-C",path,"push",target},quiet); err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func getFileMatch(root string, extension string, fullpath bool, withExtension bool) []string {
|
||||
var listFiles[]string
|
||||
err := filepath.Walk(root, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
fmt.Println(err)
|
||||
return nil
|
||||
}
|
||||
if !info.IsDir() && filepath.Ext(path) == extension {
|
||||
filepath := path
|
||||
if !fullpath {
|
||||
filepath = strings.Replace(path,fmt.Sprintf("%s/",root),"",-1)
|
||||
}
|
||||
if !withExtension {
|
||||
filepath = strings.Replace(filepath,fmt.Sprintf("%s",extension),"",-1)
|
||||
}
|
||||
listFiles = append(listFiles, filepath)
|
||||
}
|
||||
return nil
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("No files found in %s: %v\n",root, err)
|
||||
return nil
|
||||
}
|
||||
return listFiles
|
||||
}
|
||||
/*
|
||||
func gitGetWorktree(path string) (*git.Repository, *git.Worktree, error) {
|
||||
repo, err := git.PlainOpen(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Error path %s: %v\n", path, err)
|
||||
return nil,nil,err
|
||||
}
|
||||
worktree, err := repo.Worktree()
|
||||
if err != nil {
|
||||
fmt.Printf("Error worktree %s: %v\n", path, err)
|
||||
return nil,nil,err
|
||||
}
|
||||
return repo,worktree,nil
|
||||
}
|
||||
func gitPull(path string, name string) error {
|
||||
fmt.Printf("Git Pull: %#v\n", path)
|
||||
repo,worktree,err := gitGetWorktree(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Error worktree %s: %v\n", path, err)
|
||||
return err
|
||||
}
|
||||
err = worktree.Pull(&git.PullOptions{RemoteName: name})
|
||||
if err != nil {
|
||||
fmt.Printf("Error pull %s: %v\n", path, err)
|
||||
return err
|
||||
}
|
||||
if DEBUGLEVEL > 0 {
|
||||
// Print the latest commit that was just pulled
|
||||
ref, err := repo.Head()
|
||||
if err != nil {
|
||||
fmt.Printf("Error repo Head %s: %v\n", path, err)
|
||||
return err
|
||||
}
|
||||
commit, err := repo.CommitObject(ref.Hash())
|
||||
if err != nil {
|
||||
fmt.Printf("Error commit object %s: %v\n", path, err)
|
||||
return err
|
||||
}
|
||||
fmt.Println(commit)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
func gitCommit(path string, file string, msg string, name string, email string, when time.Time ) error {
|
||||
// Opens an already existing repository.
|
||||
repo,worktree,err := gitGetWorktree(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Error worktree %s: %v\n", path, err)
|
||||
return err
|
||||
}
|
||||
// Info("echo \"hello world!\" > example-git-file")
|
||||
//filename := filepath.Join(path, file)
|
||||
//err = ioutil.WriteFile(filename, []byte("hello world!"), 0644)
|
||||
// Adds the new file to the staging area.
|
||||
_, err = worktree.Add(file)
|
||||
if err != nil {
|
||||
fmt.Printf("Error git add %s: %v\n", file, err)
|
||||
return err
|
||||
}
|
||||
fmt.Printf("git add %s\n",file)
|
||||
if DEBUGLEVEL > 0 {
|
||||
status, err := worktree.Status()
|
||||
if err != nil {
|
||||
fmt.Printf("Error git status %s: %v\n", file, err)
|
||||
return err
|
||||
}
|
||||
fmt.Println(status)
|
||||
}
|
||||
// Commits the current staging area to the repository, with the new file
|
||||
// just created. We should provide the object.Signature of Author of the
|
||||
// commit Since version 5.0.1, we can omit the Author signature, being read
|
||||
// from the git config files.
|
||||
// Info("git commit -m \"example go-git commit\"")
|
||||
commit, err := worktree.Commit(msg, &git.CommitOptions{
|
||||
Author: &object.Signature{
|
||||
Name: name,
|
||||
Email: email,
|
||||
When: when,
|
||||
},
|
||||
})
|
||||
if err != nil {
|
||||
fmt.Printf("Error git create commit %s: %v\n", file, err)
|
||||
return err
|
||||
}
|
||||
// Prints the current HEAD to verify that all worked well.
|
||||
// Info("git show -s")
|
||||
obj, err := repo.CommitObject(commit)
|
||||
if err != nil {
|
||||
fmt.Printf("Error git commit %s: %v\n", file, err)
|
||||
return err
|
||||
}
|
||||
fmt.Println(obj)
|
||||
return nil
|
||||
}
|
||||
*/
|
175
websrvr.go
Normal file
175
websrvr.go
Normal file
@ -0,0 +1,175 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
cfg "github.com/jesusperez/cfgsrv"
|
||||
log "github.com/sirupsen/logrus"
|
||||
|
||||
"github.com/casbin/casbin/v2"
|
||||
|
||||
"github.com/gin-contrib/cors"
|
||||
"github.com/gin-contrib/multitemplate"
|
||||
"github.com/gin-gonic/gin"
|
||||
"github.com/pkg/browser"
|
||||
)
|
||||
|
||||
func loadTemplates(templatesDir string,templatesFiles map[string]cfg.TemplateItem,includesPath string, layoutsPath string) multitemplate.Renderer {
|
||||
r := multitemplate.NewRenderer()
|
||||
layouts, err := filepath.Glob(strings.Replace(layoutsPath,"@",(templatesDir+"/"),-1) + "/*.html")
|
||||
if err != nil {
|
||||
log.Fatalf("Error auth model loaded: %v\n", err)
|
||||
}
|
||||
includes, err := filepath.Glob(strings.Replace(includesPath,"@",(templatesDir+"/"),-1) + "/*.html")
|
||||
if err != nil {
|
||||
log.Fatalf("Error auth model loaded: %v\n", err)
|
||||
}
|
||||
for ky, tpl := range templatesFiles {
|
||||
if strings.Contains(tpl.Path,"@") {
|
||||
r.AddFromFiles(ky,templatesDir + strings.Replace(tpl.Path,"@","/",-1))
|
||||
} else {
|
||||
r.AddFromFiles(ky,tpl.Path)
|
||||
}
|
||||
}
|
||||
// Generate our templates map from our layouts/ and includes/ directories
|
||||
for _, include := range includes {
|
||||
layoutCopy := make([]string, len(layouts))
|
||||
copy(layoutCopy, layouts)
|
||||
files := append(layoutCopy, include)
|
||||
r.AddFromFiles(filepath.Base(include), files...)
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func run_web(cfg *cfg.Config,users *UsersAccounts,mdlsUsrs map[string]ModelUser, runFlags *RunFlags) {
|
||||
// logger := log.New(os.Stderr, "", 0)
|
||||
var err error
|
||||
var enforcer *casbin.Enforcer
|
||||
if cfg.UseAuthz {
|
||||
enforcer,err = casbin.NewEnforcer(cfg.AuthzModel, cfg.AuthzPolicy)
|
||||
// load the casbin model and policy from files, database is also supported.
|
||||
if err != nil {
|
||||
log.Fatalf("Error auth model loaded: %v\n",err)
|
||||
}
|
||||
}
|
||||
// logger.Println("[WARNING] DON'T USE THE EMBED CERTS FROM THIS EXAMPLE IN PRODUCTION ENVIRONMENT, GENERATE YOUR OWN!")
|
||||
gin.SetMode(gin.ReleaseMode)
|
||||
router := gin.Default()
|
||||
|
||||
corsConfig := cors.Config{
|
||||
AllowOrigins: cfg.AllowOrigins, // []string{"*"}, // , // []string{"http://localhost:3333"},
|
||||
AllowMethods: []string{"GET", "POST", "PUT", "PATCH", "DELETE", "HEAD"},
|
||||
AllowHeaders: []string{"Origin", "Authorization","Content-Length", "Content-Type","X-Request-With","Accept","Accept-Encoding"},
|
||||
ExposeHeaders: []string{"Content-Length"},
|
||||
AllowCredentials: true,
|
||||
MaxAge: 12 * time.Hour,
|
||||
// // AllowOriginFunc: func(origin string) bool {
|
||||
// // return origin == "https://github.com"
|
||||
// // },
|
||||
// MaxAge: 12 * time.Hour,
|
||||
}
|
||||
// corsConfig := cors.DefaultConfig()
|
||||
// corsConfig.AllowAllOrigins = []string{"http://localhost:3333'"}
|
||||
// corsConfig.AllowCredentials = true
|
||||
// c.Header("Access-Control-Allow-Origin", "*")
|
||||
// c.Header("Access-Control-Allow-Credentials", "true")
|
||||
// c.Header("Access-Control-Allow-Headers", "Content-Type, Content-Length, Accept-Encoding, X-CSRF-Token, Authorization, accept, origin, Cache-Control, X-Requested-With")
|
||||
// c.Header("Access-Control-Allow-Methods", "POST,HEAD,PATCH, OPTIONS, GET, PUT")
|
||||
|
||||
corsConfig.AllowOrigins = cfg.AllowOrigins // []string{"http://localhost:3333"},
|
||||
router.Use(cors.New(corsConfig))
|
||||
|
||||
//fmt.Printf("cfg AllowOrigin: %v\n", cfg.AllowOrigins)
|
||||
//db["jesus"] = "DONE"
|
||||
// listTemplates := getFileMatch(cfg.TemplatesRoot,cfg.TemplatesExt)
|
||||
//fmt.Printf("templates list: %v\n", listTemplates)
|
||||
log.WithFields(log.Fields{
|
||||
"run": "run_web",
|
||||
}).Info(fmt.Sprintf("templates path: %v\n", cfg.TemplatesFiles))
|
||||
//var templ
|
||||
//templ := template.Must(template.New("").ParseFS(f,cfg.TemplatesPaths[:]...))
|
||||
//templ := template.Must(template.ParseFS(f,cfg.TemplatesPaths[:]...))
|
||||
//templ := template.Must(template.ParseFS(f,"templates"))
|
||||
//templ := template.Must(template.ParseFS(f,cfg.TemplatesPaths[:]...))
|
||||
//router.SetHTMLTemplate(templ)
|
||||
//router.LoadHTMLGlob("templates/*.tmpl")
|
||||
router.HTMLRender = loadTemplates(cfg.TemplatesRoot,cfg.TemplatesFiles,cfg.TemplatesIncludes,cfg.TemplatesLayouts)
|
||||
// router.Use(ginI18n.Localize(
|
||||
// ginI18n.WithGetLngHandle(
|
||||
// func(context *gin.Context, defaultLng string) string {
|
||||
// lng := context.Query("lng")
|
||||
// if lng == "" {
|
||||
// return defaultLng
|
||||
// }
|
||||
// return lng
|
||||
// },
|
||||
// ),
|
||||
// ))
|
||||
// apply i18n middleware
|
||||
|
||||
// router.Use(func(c *gin.Context) {
|
||||
// c.Set("cfg",cfg)
|
||||
// // c.String(http.StatusNotFound, "Requested url does not exist")
|
||||
// //c.Abort()
|
||||
// c.Next()
|
||||
// })
|
||||
|
||||
// router.StaticFS("/public", http.FS(f))
|
||||
router.Static(cfg.AssetsURL,cfg.AssetsPath)
|
||||
var store interface{}
|
||||
if len(cfg.RedisHost) > 0 {
|
||||
store = load_redis(cfg)
|
||||
}
|
||||
|
||||
getRoutes(router,cfg,users,mdlsUsrs,enforcer,&store,runFlags)
|
||||
|
||||
hostport := fmt.Sprintf("%s:%d",cfg.Host,cfg.Port)
|
||||
log.WithFields(log.Fields{
|
||||
"run": "run_web",
|
||||
}).Info(fmt.Sprintf("Running on %s://%s",cfg.Protocol,hostport))
|
||||
if cfg.OpenBrowser {
|
||||
if len(runFlags.url) == 0 {
|
||||
browser.OpenURL(fmt.Sprintf("%s://%s",cfg.Protocol,hostport))
|
||||
} else {
|
||||
browser.OpenURL(fmt.Sprintf(runFlags.url))
|
||||
}
|
||||
}
|
||||
err = router.Run(hostport)
|
||||
if err != nil {
|
||||
log.Fatalf("Error run webserver: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func webservice(runFlags RunFlags) int {
|
||||
cfg,err := loadDataConfig(runFlags)
|
||||
//fmt.Printf("Loaded config: %#v\n", cfg)
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
if cfg.GenDist {
|
||||
errModel := createRootModels(cfg)
|
||||
if errModel != nil {
|
||||
fmt.Printf("Error createRootModels: %v\n",errModel)
|
||||
return 1
|
||||
}
|
||||
generr := genJsonModels(cfg)
|
||||
if generr != nil {
|
||||
fmt.Printf("Error genJsonModels: %v\n",generr)
|
||||
return 1
|
||||
}
|
||||
}
|
||||
acc,err := loadUsersAccounts(cfg.UsersPath)
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
mdlsUsrs,err := loadModelsUsers(cfg.UsersModelsPath)
|
||||
if err != nil {
|
||||
return 1
|
||||
}
|
||||
setLog(cfg)
|
||||
run_web(cfg,acc,mdlsUsrs,&runFlags)
|
||||
return 0
|
||||
}
|
Loading…
Reference in New Issue
Block a user