chore: code tested for cvgen genadmin

This commit is contained in:
JesusPerez 2022-01-10 09:53:01 +00:00
parent 107acf491f
commit a91aa9dac3
23 changed files with 3040 additions and 2 deletions

157
.gitignore vendored Normal file
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
View 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
}