Compare commits
30 Commits
ad1f9233ca
...
4ee241714b
Author | SHA1 | Date | |
---|---|---|---|
|
4ee241714b | ||
|
d7dc743fa2 | ||
|
c220cbb767 | ||
0dc322729f | |||
726d6dd338 | |||
a8b202bd79 | |||
6dbd250694 | |||
cc760e7698 | |||
8648ea599c | |||
77e4c5d43a | |||
b15ba367c4 | |||
4db862bc97 | |||
6ad67f6adc | |||
0caf82a27e | |||
a430857309 | |||
2643c0c9ff | |||
b8852e1ab3 | |||
0dea34daab | |||
8057313c78 | |||
1bd96d0689 | |||
bbfe46f162 | |||
eff2cbde8b | |||
5c85ecffd1 | |||
8a03b05c90 | |||
5a38b40eaf | |||
909d1f3409 | |||
66cf90f044 | |||
74d5da3987 | |||
f215159725 | |||
a6301163eb |
@ -69,13 +69,11 @@ func main() {
|
|||||||
var resourceDir string
|
var resourceDir string
|
||||||
var size uint
|
var size uint
|
||||||
var engineDebug bool
|
var engineDebug bool
|
||||||
var stateDebug bool
|
|
||||||
var host string
|
var host string
|
||||||
var port uint
|
var port uint
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
||||||
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
||||||
flag.BoolVar(&engineDebug, "engine-debug", false, "use engine debug output")
|
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
||||||
flag.BoolVar(&stateDebug, "state-debug", false, "use engine debug output")
|
|
||||||
flag.UintVar(&size, "s", 160, "max size of output")
|
flag.UintVar(&size, "s", 160, "max size of output")
|
||||||
flag.StringVar(&host, "h", "127.0.0.1", "http host")
|
flag.StringVar(&host, "h", "127.0.0.1", "http host")
|
||||||
flag.UintVar(&port, "p", 7123, "http port")
|
flag.UintVar(&port, "p", 7123, "http port")
|
||||||
@ -91,9 +89,7 @@ func main() {
|
|||||||
OutputSize: uint32(size),
|
OutputSize: uint32(size),
|
||||||
FlagCount: uint32(16),
|
FlagCount: uint32(16),
|
||||||
}
|
}
|
||||||
if stateDebug {
|
|
||||||
cfg.StateDebug = true
|
|
||||||
}
|
|
||||||
if engineDebug {
|
if engineDebug {
|
||||||
cfg.EngineDebug = true
|
cfg.EngineDebug = true
|
||||||
}
|
}
|
||||||
|
@ -41,14 +41,12 @@ func main() {
|
|||||||
var resourceDir string
|
var resourceDir string
|
||||||
var size uint
|
var size uint
|
||||||
var engineDebug bool
|
var engineDebug bool
|
||||||
var stateDebug bool
|
|
||||||
var host string
|
var host string
|
||||||
var port uint
|
var port uint
|
||||||
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
||||||
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
||||||
flag.BoolVar(&engineDebug, "engine-debug", false, "use engine debug output")
|
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
||||||
flag.BoolVar(&stateDebug, "state-debug", false, "use engine debug output")
|
|
||||||
flag.UintVar(&size, "s", 160, "max size of output")
|
flag.UintVar(&size, "s", 160, "max size of output")
|
||||||
flag.StringVar(&host, "h", "127.0.0.1", "http host")
|
flag.StringVar(&host, "h", "127.0.0.1", "http host")
|
||||||
flag.UintVar(&port, "p", 7123, "http port")
|
flag.UintVar(&port, "p", 7123, "http port")
|
||||||
@ -64,9 +62,7 @@ func main() {
|
|||||||
OutputSize: uint32(size),
|
OutputSize: uint32(size),
|
||||||
FlagCount: uint32(16),
|
FlagCount: uint32(16),
|
||||||
}
|
}
|
||||||
if stateDebug {
|
|
||||||
cfg.StateDebug = true
|
|
||||||
}
|
|
||||||
if engineDebug {
|
if engineDebug {
|
||||||
cfg.EngineDebug = true
|
cfg.EngineDebug = true
|
||||||
}
|
}
|
||||||
|
@ -30,13 +30,11 @@ func main() {
|
|||||||
var resourceDir string
|
var resourceDir string
|
||||||
var size uint
|
var size uint
|
||||||
var engineDebug bool
|
var engineDebug bool
|
||||||
var stateDebug bool
|
|
||||||
var host string
|
var host string
|
||||||
var port uint
|
var port uint
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
||||||
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
||||||
flag.BoolVar(&engineDebug, "engine-debug", false, "use engine debug output")
|
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
||||||
flag.BoolVar(&stateDebug, "state-debug", false, "use engine debug output")
|
|
||||||
flag.UintVar(&size, "s", 160, "max size of output")
|
flag.UintVar(&size, "s", 160, "max size of output")
|
||||||
flag.StringVar(&host, "h", "127.0.0.1", "http host")
|
flag.StringVar(&host, "h", "127.0.0.1", "http host")
|
||||||
flag.UintVar(&port, "p", 7123, "http port")
|
flag.UintVar(&port, "p", 7123, "http port")
|
||||||
@ -52,9 +50,7 @@ func main() {
|
|||||||
OutputSize: uint32(size),
|
OutputSize: uint32(size),
|
||||||
FlagCount: uint32(16),
|
FlagCount: uint32(16),
|
||||||
}
|
}
|
||||||
if stateDebug {
|
|
||||||
cfg.StateDebug = true
|
|
||||||
}
|
|
||||||
if engineDebug {
|
if engineDebug {
|
||||||
cfg.EngineDebug = true
|
cfg.EngineDebug = true
|
||||||
}
|
}
|
||||||
|
@ -23,10 +23,10 @@ func main() {
|
|||||||
var dbDir string
|
var dbDir string
|
||||||
var size uint
|
var size uint
|
||||||
var sessionId string
|
var sessionId string
|
||||||
var debug bool
|
var engineDebug bool
|
||||||
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
flag.StringVar(&sessionId, "session-id", "075xx2123", "session id")
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
||||||
flag.BoolVar(&debug, "d", false, "use engine debug output")
|
flag.BoolVar(&engineDebug, "d", false, "use engine debug output")
|
||||||
flag.UintVar(&size, "s", 160, "max size of output")
|
flag.UintVar(&size, "s", 160, "max size of output")
|
||||||
flag.Parse()
|
flag.Parse()
|
||||||
|
|
||||||
@ -93,7 +93,7 @@ func main() {
|
|||||||
|
|
||||||
en := lhs.GetEngine()
|
en := lhs.GetEngine()
|
||||||
en = en.WithFirst(hl.Init)
|
en = en.WithFirst(hl.Init)
|
||||||
if debug {
|
if engineDebug {
|
||||||
en = en.WithDebug(nil)
|
en = en.WithDebug(nil)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,34 +0,0 @@
|
|||||||
# URDT-USSD SSH server
|
|
||||||
|
|
||||||
An SSH server entry point for the vise engine.
|
|
||||||
|
|
||||||
|
|
||||||
## Adding public keys for access
|
|
||||||
|
|
||||||
Map your (client) public key to a session identifier (e.g. phone number)
|
|
||||||
|
|
||||||
```
|
|
||||||
go run -v -tags logtrace ./cmd/ssh/sshkey/main.go -i <session_id> [--dbdir <dbpath>] <client_publickey_filepath>
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Create a private key for the server
|
|
||||||
|
|
||||||
```
|
|
||||||
ssh-keygen -N "" -f <server_privatekey_filepath>
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Run the server
|
|
||||||
|
|
||||||
|
|
||||||
```
|
|
||||||
go run -v -tags logtrace ./cmd/ssh/main.go -h <host> -p <port> [--dbdir <dbpath>] <server_privatekey_filepath>
|
|
||||||
```
|
|
||||||
|
|
||||||
|
|
||||||
## Connect to the server
|
|
||||||
|
|
||||||
```
|
|
||||||
ssh [-v] -T -p <port> -i <client_publickey_filepath> <host>
|
|
||||||
```
|
|
115
cmd/ssh/main.go
115
cmd/ssh/main.go
@ -1,115 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"path"
|
|
||||||
"os"
|
|
||||||
"os/signal"
|
|
||||||
"sync"
|
|
||||||
"syscall"
|
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/db"
|
|
||||||
"git.defalsify.org/vise.git/engine"
|
|
||||||
"git.defalsify.org/vise.git/logging"
|
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
wg sync.WaitGroup
|
|
||||||
keyStore db.Db
|
|
||||||
logg = logging.NewVanilla()
|
|
||||||
scriptDir = path.Join("services", "registration")
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var dbDir string
|
|
||||||
var resourceDir string
|
|
||||||
var size uint
|
|
||||||
var engineDebug bool
|
|
||||||
var stateDebug bool
|
|
||||||
var host string
|
|
||||||
var port uint
|
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
|
||||||
flag.StringVar(&resourceDir, "resourcedir", path.Join("services", "registration"), "resource dir")
|
|
||||||
flag.BoolVar(&engineDebug, "engine-debug", false, "use engine debug output")
|
|
||||||
flag.BoolVar(&stateDebug, "state-debug", false, "use engine debug output")
|
|
||||||
flag.UintVar(&size, "s", 160, "max size of output")
|
|
||||||
flag.StringVar(&host, "h", "127.0.0.1", "http host")
|
|
||||||
flag.UintVar(&port, "p", 7122, "http port")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
sshKeyFile := flag.Arg(0)
|
|
||||||
_, err := os.Stat(sshKeyFile)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "cannot open ssh server private key file: %v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
logg.WarnCtxf(ctx, "!!!!! WARNING WARNING WARNING")
|
|
||||||
logg.WarnCtxf(ctx, "!!!!! =======================")
|
|
||||||
logg.WarnCtxf(ctx, "!!!!! This is not a production ready server!")
|
|
||||||
logg.WarnCtxf(ctx, "!!!!! Do not expose to internet and only use with tunnel!")
|
|
||||||
logg.WarnCtxf(ctx, "!!!!! (See ssh -L <...>)")
|
|
||||||
|
|
||||||
logg.Infof("start command", "dbdir", dbDir, "resourcedir", resourceDir, "outputsize", size, "keyfile", sshKeyFile, "host", host, "port", port)
|
|
||||||
|
|
||||||
pfp := path.Join(scriptDir, "pp.csv")
|
|
||||||
|
|
||||||
cfg := engine.Config{
|
|
||||||
Root: "root",
|
|
||||||
OutputSize: uint32(size),
|
|
||||||
FlagCount: uint32(16),
|
|
||||||
}
|
|
||||||
if stateDebug {
|
|
||||||
cfg.StateDebug = true
|
|
||||||
}
|
|
||||||
if engineDebug {
|
|
||||||
cfg.EngineDebug = true
|
|
||||||
}
|
|
||||||
|
|
||||||
authKeyStore, err := ssh.NewSshKeyStore(ctx, dbDir)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "keystore file open error: %v", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer func () {
|
|
||||||
logg.TraceCtxf(ctx, "shutdown auth key store reached")
|
|
||||||
err = authKeyStore.Close()
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "keystore close error", "err", err)
|
|
||||||
}
|
|
||||||
}()
|
|
||||||
|
|
||||||
cint := make(chan os.Signal)
|
|
||||||
cterm := make(chan os.Signal)
|
|
||||||
signal.Notify(cint, os.Interrupt, syscall.SIGINT)
|
|
||||||
signal.Notify(cterm, os.Interrupt, syscall.SIGTERM)
|
|
||||||
|
|
||||||
runner := &ssh.SshRunner{
|
|
||||||
Cfg: cfg,
|
|
||||||
Debug: engineDebug,
|
|
||||||
FlagFile: pfp,
|
|
||||||
DbDir: dbDir,
|
|
||||||
ResourceDir: resourceDir,
|
|
||||||
SrvKeyFile: sshKeyFile,
|
|
||||||
Host: host,
|
|
||||||
Port: port,
|
|
||||||
}
|
|
||||||
go func() {
|
|
||||||
select {
|
|
||||||
case _ = <-cint:
|
|
||||||
case _ = <-cterm:
|
|
||||||
}
|
|
||||||
logg.TraceCtxf(ctx, "shutdown runner reached")
|
|
||||||
err := runner.Stop()
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "runner stop error", "err", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
}()
|
|
||||||
runner.Run(ctx, authKeyStore)
|
|
||||||
}
|
|
@ -1,44 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/ssh"
|
|
||||||
)
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
var dbDir string
|
|
||||||
var sessionId string
|
|
||||||
flag.StringVar(&dbDir, "dbdir", ".state", "database dir to read from")
|
|
||||||
flag.StringVar(&sessionId, "i", "", "session id")
|
|
||||||
flag.Parse()
|
|
||||||
|
|
||||||
if sessionId == "" {
|
|
||||||
fmt.Fprintf(os.Stderr, "empty session id\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
ctx := context.Background()
|
|
||||||
|
|
||||||
sshKeyFile := flag.Arg(0)
|
|
||||||
if sshKeyFile == "" {
|
|
||||||
fmt.Fprintf(os.Stderr, "missing key file argument\n")
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
|
|
||||||
store, err := ssh.NewSshKeyStore(ctx, dbDir)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
defer store.Close()
|
|
||||||
|
|
||||||
err = store.AddFromFile(ctx, sshKeyFile, sessionId)
|
|
||||||
if err != nil {
|
|
||||||
fmt.Fprintf(os.Stderr, "%v\n", err)
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
@ -88,7 +88,6 @@ func (ls *LocalHandlerService) GetHandler() (*ussd.Handlers, error) {
|
|||||||
ls.DbRs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo)
|
ls.DbRs.AddLocalFunc("get_profile_info", ussdHandlers.GetProfileInfo)
|
||||||
ls.DbRs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob)
|
ls.DbRs.AddLocalFunc("verify_yob", ussdHandlers.VerifyYob)
|
||||||
ls.DbRs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob)
|
ls.DbRs.AddLocalFunc("reset_incorrect_date_format", ussdHandlers.ResetIncorrectYob)
|
||||||
ls.DbRs.AddLocalFunc("set_reset_single_edit", ussdHandlers.SetResetSingleEdit)
|
|
||||||
ls.DbRs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction)
|
ls.DbRs.AddLocalFunc("initiate_transaction", ussdHandlers.InitiateTransaction)
|
||||||
ls.DbRs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin)
|
ls.DbRs.AddLocalFunc("save_temporary_pin", ussdHandlers.SaveTemporaryPin)
|
||||||
ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin)
|
ls.DbRs.AddLocalFunc("verify_new_pin", ussdHandlers.VerifyNewPin)
|
||||||
|
@ -117,17 +117,14 @@ func (h *Handlers) Init(ctx context.Context, sym string, input []byte) (resource
|
|||||||
func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) SetLanguage(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
|
|
||||||
sym, _ = h.st.Where()
|
symbol, _ := h.st.Where()
|
||||||
|
code := strings.Split(symbol, "_")[1]
|
||||||
|
|
||||||
switch sym {
|
if !utils.IsValidISO639(code) {
|
||||||
case "set_default":
|
return res, nil
|
||||||
res.FlagSet = append(res.FlagSet, state.FLAG_LANG)
|
|
||||||
res.Content = "eng"
|
|
||||||
case "set_swa":
|
|
||||||
res.FlagSet = append(res.FlagSet, state.FLAG_LANG)
|
|
||||||
res.Content = "swa"
|
|
||||||
default:
|
|
||||||
}
|
}
|
||||||
|
res.FlagSet = append(res.FlagSet, state.FLAG_LANG)
|
||||||
|
res.Content = code
|
||||||
|
|
||||||
languageSetFlag, err := h.flagManager.GetFlag("flag_language_set")
|
languageSetFlag, err := h.flagManager.GetFlag("flag_language_set")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -279,32 +276,6 @@ func (h *Handlers) ConfirmPinChange(ctx context.Context, sym string, input []byt
|
|||||||
return res, nil
|
return res, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// SetResetSingleEdit sets and resets flags to allow gradual editing of profile information.
|
|
||||||
func (h *Handlers) SetResetSingleEdit(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
|
||||||
var res resource.Result
|
|
||||||
|
|
||||||
menuOption := string(input)
|
|
||||||
|
|
||||||
flag_allow_update, _ := h.flagManager.GetFlag("flag_allow_update")
|
|
||||||
flag_single_edit, _ := h.flagManager.GetFlag("flag_single_edit")
|
|
||||||
|
|
||||||
switch menuOption {
|
|
||||||
case "2":
|
|
||||||
res.FlagReset = append(res.FlagReset, flag_allow_update)
|
|
||||||
res.FlagSet = append(res.FlagSet, flag_single_edit)
|
|
||||||
case "3":
|
|
||||||
res.FlagReset = append(res.FlagReset, flag_allow_update)
|
|
||||||
res.FlagSet = append(res.FlagSet, flag_single_edit)
|
|
||||||
case "4":
|
|
||||||
res.FlagReset = append(res.FlagReset, flag_allow_update)
|
|
||||||
res.FlagSet = append(res.FlagSet, flag_single_edit)
|
|
||||||
default:
|
|
||||||
res.FlagReset = append(res.FlagReset, flag_single_edit)
|
|
||||||
}
|
|
||||||
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// VerifyPin checks whether the confirmation PIN is similar to the account PIN
|
// VerifyPin checks whether the confirmation PIN is similar to the account PIN
|
||||||
// If similar, it sets the USERFLAG_PIN_SET flag allowing the user
|
// If similar, it sets the USERFLAG_PIN_SET flag allowing the user
|
||||||
// to access the main menu
|
// to access the main menu
|
||||||
@ -435,6 +406,7 @@ func (h *Handlers) SaveLocation(ctx context.Context, sym string, input []byte) (
|
|||||||
|
|
||||||
// SaveGender updates the gender in the gdbm with the provided input.
|
// SaveGender updates the gender in the gdbm with the provided input.
|
||||||
func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (resource.Result, error) {
|
||||||
|
symbol, _ := h.st.Where()
|
||||||
var res resource.Result
|
var res resource.Result
|
||||||
var err error
|
var err error
|
||||||
sessionId, ok := ctx.Value("SessionId").(string)
|
sessionId, ok := ctx.Value("SessionId").(string)
|
||||||
@ -442,21 +414,11 @@ func (h *Handlers) SaveGender(ctx context.Context, sym string, input []byte) (re
|
|||||||
return res, fmt.Errorf("missing session")
|
return res, fmt.Errorf("missing session")
|
||||||
}
|
}
|
||||||
|
|
||||||
if len(input) > 0 {
|
gender := strings.Split(symbol, "_")[1]
|
||||||
gender := string(input)
|
store := h.userdataStore
|
||||||
switch gender {
|
err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender))
|
||||||
case "1":
|
if err != nil {
|
||||||
gender = "Male"
|
return res, nil
|
||||||
case "2":
|
|
||||||
gender = "Female"
|
|
||||||
case "3":
|
|
||||||
gender = "Unspecified"
|
|
||||||
}
|
|
||||||
store := h.userdataStore
|
|
||||||
err = store.WriteEntry(ctx, sessionId, utils.DATA_GENDER, []byte(gender))
|
|
||||||
if err != nil {
|
|
||||||
return res, nil
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res, nil
|
return res, nil
|
||||||
|
@ -1,64 +0,0 @@
|
|||||||
package ssh
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"path"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/db"
|
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
|
||||||
)
|
|
||||||
|
|
||||||
type SshKeyStore struct {
|
|
||||||
store db.Db
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewSshKeyStore(ctx context.Context, dbDir string) (*SshKeyStore, error) {
|
|
||||||
keyStore := &SshKeyStore{}
|
|
||||||
keyStoreFile := path.Join(dbDir, "ssh_authorized_keys.gdbm")
|
|
||||||
keyStore.store = storage.NewThreadGdbmDb()
|
|
||||||
err := keyStore.store.Connect(ctx, keyStoreFile)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return keyStore, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func(s *SshKeyStore) AddFromFile(ctx context.Context, fp string, sessionId string) error {
|
|
||||||
_, err := os.Stat(fp)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("cannot open ssh server public key file: %v\n", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
publicBytes, err := os.ReadFile(fp)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed to load public key: %v", err)
|
|
||||||
}
|
|
||||||
pubKey, _, _, _, err := ssh.ParseAuthorizedKey(publicBytes)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("Failed to parse public key: %v", err)
|
|
||||||
}
|
|
||||||
k := append([]byte{0x01}, pubKey.Marshal()...)
|
|
||||||
s.store.SetPrefix(storage.DATATYPE_CUSTOM)
|
|
||||||
logg.Infof("Added key", "sessionId", sessionId, "public key", string(publicBytes))
|
|
||||||
return s.store.Put(ctx, k, []byte(sessionId))
|
|
||||||
}
|
|
||||||
|
|
||||||
func(s *SshKeyStore) Get(ctx context.Context, pubKey ssh.PublicKey) (string, error) {
|
|
||||||
s.store.SetLanguage(nil)
|
|
||||||
s.store.SetPrefix(storage.DATATYPE_CUSTOM)
|
|
||||||
k := append([]byte{0x01}, pubKey.Marshal()...)
|
|
||||||
v, err := s.store.Get(ctx, k)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
}
|
|
||||||
return string(v), nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func(s *SshKeyStore) Close() error {
|
|
||||||
return s.store.Close()
|
|
||||||
}
|
|
@ -1,284 +0,0 @@
|
|||||||
package ssh
|
|
||||||
|
|
||||||
import (
|
|
||||||
"context"
|
|
||||||
"encoding/hex"
|
|
||||||
"encoding/base64"
|
|
||||||
"errors"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"sync"
|
|
||||||
|
|
||||||
"golang.org/x/crypto/ssh"
|
|
||||||
|
|
||||||
"git.defalsify.org/vise.git/engine"
|
|
||||||
"git.defalsify.org/vise.git/logging"
|
|
||||||
"git.defalsify.org/vise.git/resource"
|
|
||||||
"git.defalsify.org/vise.git/state"
|
|
||||||
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/handlers"
|
|
||||||
"git.grassecon.net/urdt/ussd/internal/storage"
|
|
||||||
)
|
|
||||||
|
|
||||||
var (
|
|
||||||
logg = logging.NewVanilla().WithDomain("ssh")
|
|
||||||
)
|
|
||||||
|
|
||||||
type auther struct {
|
|
||||||
Ctx context.Context
|
|
||||||
keyStore *SshKeyStore
|
|
||||||
auth map[string]string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewAuther(ctx context.Context, keyStore *SshKeyStore) *auther {
|
|
||||||
return &auther{
|
|
||||||
Ctx: ctx,
|
|
||||||
keyStore: keyStore,
|
|
||||||
auth: make(map[string]string),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func(a *auther) Check(conn ssh.ConnMetadata, pubKey ssh.PublicKey) (*ssh.Permissions, error) {
|
|
||||||
va, err := a.keyStore.Get(a.Ctx, pubKey)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ka := hex.EncodeToString(conn.SessionID())
|
|
||||||
a.auth[ka] = va
|
|
||||||
fmt.Fprintf(os.Stderr, "connect: %s -> %s\n", ka, va)
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func(a *auther) FromConn(c *ssh.ServerConn) (string, error) {
|
|
||||||
if c == nil {
|
|
||||||
return "", errors.New("nil server conn")
|
|
||||||
}
|
|
||||||
if c.Conn == nil {
|
|
||||||
return "", errors.New("nil underlying conn")
|
|
||||||
}
|
|
||||||
return a.Get(c.Conn.SessionID())
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
func(a *auther) Get(k []byte) (string, error) {
|
|
||||||
ka := hex.EncodeToString(k)
|
|
||||||
v, ok := a.auth[ka]
|
|
||||||
if !ok {
|
|
||||||
return "", errors.New("not found")
|
|
||||||
}
|
|
||||||
return v, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func(s *SshRunner) serve(ctx context.Context, sessionId string, ch ssh.NewChannel, en engine.Engine) error {
|
|
||||||
if ch == nil {
|
|
||||||
return errors.New("nil channel")
|
|
||||||
}
|
|
||||||
if ch.ChannelType() != "session" {
|
|
||||||
ch.Reject(ssh.UnknownChannelType, "that is not the channel you are looking for")
|
|
||||||
return errors.New("not a session")
|
|
||||||
}
|
|
||||||
channel, requests, err := ch.Accept()
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
defer channel.Close()
|
|
||||||
s.wg.Add(1)
|
|
||||||
go func(reqIn <-chan *ssh.Request) {
|
|
||||||
defer s.wg.Done()
|
|
||||||
for req := range reqIn {
|
|
||||||
req.Reply(req.Type == "shell", nil)
|
|
||||||
}
|
|
||||||
_ = requests
|
|
||||||
}(requests)
|
|
||||||
|
|
||||||
cont, err := en.Exec(ctx, []byte{})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("initial engine exec err: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
var input [state.INPUT_LIMIT]byte
|
|
||||||
for cont {
|
|
||||||
c, err := en.Flush(ctx, channel)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("flush err: %v", err)
|
|
||||||
}
|
|
||||||
_, err = channel.Write([]byte{0x0a})
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("newline err: %v", err)
|
|
||||||
}
|
|
||||||
c, err = channel.Read(input[:])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("read input fail: %v", err)
|
|
||||||
}
|
|
||||||
logg.TraceCtxf(ctx, "input read", "c", c, "input", input[:c-1])
|
|
||||||
cont, err = en.Exec(ctx, input[:c-1])
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("engine exec err: %v", err)
|
|
||||||
}
|
|
||||||
logg.TraceCtxf(ctx, "exec cont", "cont", cont, "en", en)
|
|
||||||
_ = c
|
|
||||||
}
|
|
||||||
c, err := en.Flush(ctx, channel)
|
|
||||||
if err != nil {
|
|
||||||
return fmt.Errorf("last flush err: %v", err)
|
|
||||||
}
|
|
||||||
_ = c
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
type SshRunner struct {
|
|
||||||
Ctx context.Context
|
|
||||||
Cfg engine.Config
|
|
||||||
FlagFile string
|
|
||||||
DbDir string
|
|
||||||
ResourceDir string
|
|
||||||
Debug bool
|
|
||||||
SrvKeyFile string
|
|
||||||
Host string
|
|
||||||
Port uint
|
|
||||||
wg sync.WaitGroup
|
|
||||||
lst net.Listener
|
|
||||||
}
|
|
||||||
|
|
||||||
func(s *SshRunner) Stop() error {
|
|
||||||
return s.lst.Close()
|
|
||||||
}
|
|
||||||
|
|
||||||
func(s *SshRunner) GetEngine(sessionId string) (engine.Engine, func(), error) {
|
|
||||||
ctx := s.Ctx
|
|
||||||
menuStorageService := storage.NewMenuStorageService(s.DbDir, s.ResourceDir)
|
|
||||||
|
|
||||||
err := menuStorageService.EnsureDbDir()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
rs, err := menuStorageService.GetResource(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
pe, err := menuStorageService.GetPersister(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
userdatastore, err := menuStorageService.GetUserdataDb(ctx)
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
dbResource, ok := rs.(*resource.DbResource)
|
|
||||||
if !ok {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
lhs, err := handlers.NewLocalHandlerService(s.FlagFile, true, dbResource, s.Cfg, rs)
|
|
||||||
lhs.SetDataStore(&userdatastore)
|
|
||||||
lhs.SetPersister(pe)
|
|
||||||
lhs.Cfg.SessionId = sessionId
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
hl, err := lhs.GetHandler()
|
|
||||||
if err != nil {
|
|
||||||
return nil, nil, err
|
|
||||||
}
|
|
||||||
|
|
||||||
en := lhs.GetEngine()
|
|
||||||
en = en.WithFirst(hl.Init)
|
|
||||||
if s.Debug {
|
|
||||||
en = en.WithDebug(nil)
|
|
||||||
}
|
|
||||||
// TODO: this is getting very hacky!
|
|
||||||
closer := func() {
|
|
||||||
err := menuStorageService.Close()
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "menu storage service cleanup fail", "err", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return en, closer, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// adapted example from crypto/ssh package, NewServerConn doc
|
|
||||||
func(s *SshRunner) Run(ctx context.Context, keyStore *SshKeyStore) {
|
|
||||||
running := true
|
|
||||||
|
|
||||||
// TODO: waitgroup should probably not be global
|
|
||||||
defer s.wg.Wait()
|
|
||||||
|
|
||||||
auth := NewAuther(ctx, keyStore)
|
|
||||||
cfg := ssh.ServerConfig{
|
|
||||||
PublicKeyCallback: auth.Check,
|
|
||||||
}
|
|
||||||
|
|
||||||
privateBytes, err := os.ReadFile(s.SrvKeyFile)
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "Failed to load private key", "err", err)
|
|
||||||
}
|
|
||||||
private, err := ssh.ParsePrivateKey(privateBytes)
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "Failed to parse private key", "err", err)
|
|
||||||
}
|
|
||||||
srvPub := private.PublicKey()
|
|
||||||
srvPubStr := base64.StdEncoding.EncodeToString(srvPub.Marshal())
|
|
||||||
logg.InfoCtxf(ctx, "have server key", "type", srvPub.Type(), "public", srvPubStr)
|
|
||||||
cfg.AddHostKey(private)
|
|
||||||
|
|
||||||
s.lst, err = net.Listen("tcp", fmt.Sprintf("%s:%d", s.Host, s.Port))
|
|
||||||
if err != nil {
|
|
||||||
panic(err)
|
|
||||||
}
|
|
||||||
|
|
||||||
for running {
|
|
||||||
conn, err := s.lst.Accept()
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "ssh accept error", "err", err)
|
|
||||||
running = false
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
go func(conn net.Conn) {
|
|
||||||
defer conn.Close()
|
|
||||||
for true {
|
|
||||||
srvConn, nC, rC, err := ssh.NewServerConn(conn, &cfg)
|
|
||||||
if err != nil {
|
|
||||||
logg.InfoCtxf(ctx, "rejected client", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
logg.DebugCtxf(ctx, "ssh client connected", "conn", srvConn)
|
|
||||||
|
|
||||||
s.wg.Add(1)
|
|
||||||
go func() {
|
|
||||||
ssh.DiscardRequests(rC)
|
|
||||||
s.wg.Done()
|
|
||||||
}()
|
|
||||||
|
|
||||||
sessionId, err := auth.FromConn(srvConn)
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "Cannot find authentication")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
en, closer, err := s.GetEngine(sessionId)
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "engine won't start", "err", err)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
defer func() {
|
|
||||||
err := en.Finish()
|
|
||||||
if err != nil {
|
|
||||||
logg.ErrorCtxf(ctx, "engine won't stop", "err", err)
|
|
||||||
}
|
|
||||||
closer()
|
|
||||||
}()
|
|
||||||
for ch := range nC {
|
|
||||||
err = s.serve(ctx, sessionId, ch, en)
|
|
||||||
logg.ErrorCtxf(ctx, "ssh server finish", "err", err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}(conn)
|
|
||||||
}
|
|
||||||
}
|
|
11
internal/utils/isocode.go
Normal file
11
internal/utils/isocode.go
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
var isoCodes = map[string]bool{
|
||||||
|
"eng": true, // English
|
||||||
|
"swa": true, // Swahili
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsValidISO639(code string) bool {
|
||||||
|
return isoCodes[code]
|
||||||
|
}
|
1
services/registration/_catch
Normal file
1
services/registration/_catch
Normal file
@ -0,0 +1 @@
|
|||||||
|
Something went wrong.Please try again
|
1
services/registration/_catch.vis
Normal file
1
services/registration/_catch.vis
Normal file
@ -0,0 +1 @@
|
|||||||
|
HALT
|
@ -9,4 +9,5 @@ CATCH invalid_amount flag_invalid_amount 1
|
|||||||
INCMP _ 0
|
INCMP _ 0
|
||||||
LOAD get_recipient 12
|
LOAD get_recipient 12
|
||||||
LOAD get_sender 64
|
LOAD get_sender 64
|
||||||
|
LOAD get_amount 12
|
||||||
INCMP transaction_pin *
|
INCMP transaction_pin *
|
||||||
|
@ -11,8 +11,6 @@ MOUT view 7
|
|||||||
MOUT back 0
|
MOUT back 0
|
||||||
HALT
|
HALT
|
||||||
INCMP my_account 0
|
INCMP my_account 0
|
||||||
LOAD set_reset_single_edit 0
|
|
||||||
RELOAD set_reset_single_edit
|
|
||||||
INCMP enter_name 1
|
INCMP enter_name 1
|
||||||
INCMP enter_familyname 2
|
INCMP enter_familyname 2
|
||||||
INCMP select_gender 3
|
INCMP select_gender 3
|
||||||
|
@ -1,13 +1,15 @@
|
|||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH profile_update_success flag_allow_update 1
|
CATCH profile_update_success flag_allow_update 1
|
||||||
LOAD save_gender 0
|
|
||||||
MOUT male 1
|
MOUT male 1
|
||||||
MOUT female 2
|
MOUT female 2
|
||||||
MOUT unspecified 3
|
MOUT unspecified 3
|
||||||
MOUT back 0
|
MOUT back 0
|
||||||
HALT
|
HALT
|
||||||
RELOAD save_gender
|
|
||||||
INCMP _ 0
|
INCMP _ 0
|
||||||
INCMP pin_entry *
|
INCMP set_male 1
|
||||||
|
INCMP set_female 2
|
||||||
|
INCMP set_unspecified 3
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
MOUT english 0
|
MOUT english 0
|
||||||
MOUT kiswahili 1
|
MOUT kiswahili 1
|
||||||
HALT
|
HALT
|
||||||
INCMP set_default 0
|
INCMP set_eng 0
|
||||||
INCMP set_swa 1
|
INCMP set_swa 1
|
||||||
INCMP . *
|
INCMP . *
|
||||||
|
4
services/registration/set_female.vis
Normal file
4
services/registration/set_female.vis
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
LOAD save_gender 0
|
||||||
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
|
CATCH profile_update_success flag_allow_update 1
|
||||||
|
MOVE pin_entry
|
4
services/registration/set_male.vis
Normal file
4
services/registration/set_male.vis
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
LOAD save_gender 0
|
||||||
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
|
CATCH profile_update_success flag_allow_update 1
|
||||||
|
MOVE pin_entry
|
4
services/registration/set_unspecified.vis
Normal file
4
services/registration/set_unspecified.vis
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
LOAD save_gender 0
|
||||||
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
|
CATCH profile_update_success flag_allow_update 1
|
||||||
|
MOVE pin_entry
|
@ -1,7 +1,7 @@
|
|||||||
LOAD reset_incorrect 6
|
LOAD reset_incorrect 6
|
||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
CATCH _ flag_account_authorized 0
|
CATCH _ flag_account_authorized 0
|
||||||
LOAD get_amount 10
|
RELOAD get_amount
|
||||||
MAP get_amount
|
MAP get_amount
|
||||||
RELOAD get_recipient
|
RELOAD get_recipient
|
||||||
MAP get_recipient
|
MAP get_recipient
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
{{.get_recipient}} will receive {{.validate_amount}} from {{.get_sender}}
|
{{.get_recipient}} will receive {{.get_amount}} from {{.get_sender}}
|
||||||
Please enter your PIN to confirm:
|
Please enter your PIN to confirm:
|
@ -1,12 +1,13 @@
|
|||||||
MAP validate_amount
|
RELOAD get_amount
|
||||||
|
MAP get_amount
|
||||||
RELOAD get_recipient
|
RELOAD get_recipient
|
||||||
MAP get_recipient
|
MAP get_recipient
|
||||||
RELOAD get_sender
|
RELOAD get_sender
|
||||||
MAP get_sender
|
MAP get_sender
|
||||||
MOUT back 0
|
MOUT back 0
|
||||||
MOUT quit 9
|
MOUT quit 9
|
||||||
HALT
|
|
||||||
LOAD authorize_account 6
|
LOAD authorize_account 6
|
||||||
|
HALT
|
||||||
RELOAD authorize_account
|
RELOAD authorize_account
|
||||||
CATCH incorrect_pin flag_incorrect_pin 1
|
CATCH incorrect_pin flag_incorrect_pin 1
|
||||||
INCMP _ 0
|
INCMP _ 0
|
||||||
|
@ -1,2 +1,2 @@
|
|||||||
{{.get_recipient}} atapokea {{.validate_amount}} kutoka kwa {{.get_sender}}
|
{{.get_recipient}} atapokea {{.get_amount}} kutoka kwa {{.get_sender}}
|
||||||
Tafadhali weka PIN yako kudhibitisha:
|
Tafadhali weka PIN yako kudhibitisha:
|
Loading…
Reference in New Issue
Block a user