Implement parsing browser requests and sending errors (#9)
This commit is contained in:
parent
60a4a1ddaa
commit
42e6157238
|
@ -41,9 +41,11 @@ should be supplied as a `message` parameter.
|
|||
|
||||
## List of Error Codes
|
||||
|
||||
| Code | Description | Parameters |
|
||||
| ---- | ----------- | ---------- |
|
||||
| | | |
|
||||
| Code | Description | Parameters |
|
||||
| ---- | -------------------------------------- | ---------- |
|
||||
| 10 | Unable to parse browser request length | |
|
||||
| 11 | Unable to parse browser request | |
|
||||
| 12 | Invalid request action | action |
|
||||
|
||||
## Settings
|
||||
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
package errors
|
||||
|
||||
import (
|
||||
"os"
|
||||
)
|
||||
|
||||
// Code exit code
|
||||
type Code int
|
||||
|
||||
// Error codes that are sent to the browser extension and used as exit codes in the app.
|
||||
// DO NOT MODIFY THE VALUES, always append new error codes to the bottom.
|
||||
const (
|
||||
// CodeParseRequestLength error parsing request length
|
||||
CodeParseRequestLength Code = 10
|
||||
|
||||
// CodeParseRequest error parsing request
|
||||
CodeParseRequest Code = 11
|
||||
|
||||
// CodeInvalidRequestAction error parsing request action
|
||||
CodeInvalidRequestAction = 12
|
||||
)
|
||||
|
||||
// ExitWithCode exit with error code
|
||||
func ExitWithCode(code Code) {
|
||||
os.Exit(int(code))
|
||||
}
|
27
main.go
27
main.go
|
@ -5,35 +5,34 @@ import (
|
|||
"fmt"
|
||||
"os"
|
||||
|
||||
"github.com/maximbaz/browserpass-native/openbsd"
|
||||
"github.com/maximbaz/browserpass-native/persistentlog"
|
||||
"github.com/browserpass/browserpass-native/openbsd"
|
||||
"github.com/browserpass/browserpass-native/persistentlog"
|
||||
"github.com/browserpass/browserpass-native/request"
|
||||
"github.com/browserpass/browserpass-native/version"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
// VERSION host app version
|
||||
const VERSION = "3.0.0"
|
||||
|
||||
func main() {
|
||||
var verbose bool
|
||||
var version bool
|
||||
flag.BoolVar(&verbose, "v", false, "print verbose output")
|
||||
flag.BoolVar(&version, "version", false, "print version and exit")
|
||||
var isVerbose bool
|
||||
var isVersion bool
|
||||
flag.BoolVar(&isVerbose, "v", false, "print verbose output")
|
||||
flag.BoolVar(&isVersion, "version", false, "print version and exit")
|
||||
flag.Parse()
|
||||
|
||||
if version {
|
||||
fmt.Println("Browserpass host app version:", VERSION)
|
||||
if isVersion {
|
||||
fmt.Println("Browserpass host app version:", version.String())
|
||||
os.Exit(0)
|
||||
}
|
||||
|
||||
openbsd.Pledge("stdio rpath proc exec")
|
||||
|
||||
log.SetFormatter(&log.TextFormatter{FullTimestamp: true})
|
||||
if verbose {
|
||||
if isVerbose {
|
||||
log.SetLevel(log.DebugLevel)
|
||||
}
|
||||
|
||||
persistentlog.AddPersistentLogHook()
|
||||
|
||||
log.Debugf("Starting browserpass host app v%v", VERSION)
|
||||
process()
|
||||
log.Debugf("Starting browserpass host app v%v", version.String())
|
||||
request.Process()
|
||||
}
|
||||
|
|
|
@ -11,11 +11,9 @@ import (
|
|||
|
||||
// AddPersistentLogHook configures persisting logs in syslog
|
||||
func AddPersistentLogHook() {
|
||||
hook, err := logSyslog.NewSyslogHook("", "", syslog.LOG_INFO, "browserpass")
|
||||
|
||||
if err == nil {
|
||||
log.AddHook(hook)
|
||||
if hook, err := logSyslog.NewSyslogHook("", "", syslog.LOG_INFO, "browserpass"); err != nil {
|
||||
log.Warn("Unable to connect to syslog, logs will NOT be persisted: ", err)
|
||||
} else {
|
||||
log.Warn("Unable to connect to syslog, logs will NOT be persisted")
|
||||
log.AddHook(hook)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ func AddPersistentLogHook() {
|
|||
}
|
||||
logFolderPath := filepath.Join(appDataPath, "browserpass")
|
||||
if err := os.MkdirAll(logFolderPath, os.ModePerm); err != nil {
|
||||
log.Warn("Unable to create browserpass folder in %%APPDATA%%, logs will NOT be persisted")
|
||||
log.Warn("Unable to create browserpass folder in %%APPDATA%%, logs will NOT be persisted: ", err)
|
||||
return
|
||||
}
|
||||
logFilePath := filepath.Join(logFolderPath, "browserpass.log")
|
||||
|
|
|
@ -1,5 +0,0 @@
|
|||
package main
|
||||
|
||||
func process() {
|
||||
// read stdin, process request, print to stdout...
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
package request
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"github.com/browserpass/browserpass-native/errors"
|
||||
"github.com/browserpass/browserpass-native/response"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type request struct {
|
||||
Action string `json:"action"`
|
||||
Settings interface{} `json:"settings"`
|
||||
}
|
||||
|
||||
// Process handles browser request
|
||||
func Process() {
|
||||
requestLength := parseRequestLength()
|
||||
request := parseRequest(requestLength)
|
||||
|
||||
switch request.Action {
|
||||
case "configure":
|
||||
break
|
||||
case "list":
|
||||
break
|
||||
case "fetch":
|
||||
break
|
||||
default:
|
||||
log.Errorf("Received a browser request with an unknown action: %+v", request)
|
||||
response.SendError(errors.CodeInvalidRequestAction, "Invalid request action", &map[string]string{"action": request.Action})
|
||||
errors.ExitWithCode(errors.CodeInvalidRequestAction)
|
||||
}
|
||||
}
|
||||
|
||||
// Request length is the first 4 bytes in LittleEndian encoding on stdin
|
||||
func parseRequestLength() uint32 {
|
||||
var length uint32
|
||||
if err := binary.Read(os.Stdin, binary.LittleEndian, &length); err != nil {
|
||||
// TODO: Original browserpass ignores EOF as if it is expected, is it true?
|
||||
// if err == io.EOF {
|
||||
// return
|
||||
// }
|
||||
log.Error("Unable to parse the length of the browser request: ", err)
|
||||
response.SendError(errors.CodeParseRequestLength, "Unable to parse the length of the browser request", nil)
|
||||
errors.ExitWithCode(errors.CodeParseRequestLength)
|
||||
}
|
||||
return length
|
||||
}
|
||||
|
||||
// Request is a json with a predefined structure
|
||||
func parseRequest(messageLength uint32) request {
|
||||
var parsed request
|
||||
reader := &io.LimitedReader{R: os.Stdin, N: int64(messageLength)}
|
||||
if err := json.NewDecoder(reader).Decode(&parsed); err != nil {
|
||||
log.Error("Unable to parse the browser request: ", err)
|
||||
response.SendError(errors.CodeParseRequest, "Unable to parse the browser request", nil)
|
||||
errors.ExitWithCode(errors.CodeParseRequest)
|
||||
}
|
||||
return parsed
|
||||
}
|
|
@ -0,0 +1,65 @@
|
|||
package response
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"encoding/json"
|
||||
"os"
|
||||
|
||||
"github.com/browserpass/browserpass-native/errors"
|
||||
"github.com/browserpass/browserpass-native/version"
|
||||
log "github.com/sirupsen/logrus"
|
||||
)
|
||||
|
||||
type okResponse struct {
|
||||
Status string `json:"status"`
|
||||
Version int `json:"version"`
|
||||
Data interface{} `json:"data"`
|
||||
}
|
||||
|
||||
type errorResponse struct {
|
||||
Status string `json:"status"`
|
||||
Code errors.Code `json:"code"`
|
||||
Version int `json:"version"`
|
||||
Params interface{} `json:"params"`
|
||||
}
|
||||
|
||||
// SendError sends an error response to the browser extension in the predefined json format
|
||||
func SendError(errorCode errors.Code, errorMsg string, extraParams *map[string]string) {
|
||||
params := map[string]string{
|
||||
"message": errorMsg,
|
||||
}
|
||||
if extraParams != nil {
|
||||
for key, value := range *extraParams {
|
||||
params[key] = value
|
||||
}
|
||||
}
|
||||
send(&errorResponse{
|
||||
Status: "error",
|
||||
Code: errorCode,
|
||||
Version: version.Code,
|
||||
Params: params,
|
||||
})
|
||||
}
|
||||
|
||||
func send(data interface{}) {
|
||||
switch data.(type) {
|
||||
case *okResponse:
|
||||
case *errorResponse:
|
||||
break
|
||||
default:
|
||||
log.Fatalf("Only data of type OkResponse and ErrorResponse is allowed to be sent to the browser extension, attempted to send: %+v", data)
|
||||
}
|
||||
|
||||
var bytesBuffer bytes.Buffer
|
||||
if err := json.NewEncoder(&bytesBuffer).Encode(data); err != nil {
|
||||
log.Fatal("Unable to encode data for sending: ", err)
|
||||
}
|
||||
|
||||
if err := binary.Write(os.Stdout, binary.LittleEndian, uint32(bytesBuffer.Len())); err != nil {
|
||||
log.Fatal("Unable to send the length of the response data: ", err)
|
||||
}
|
||||
if _, err := bytesBuffer.WriteTo(os.Stdout); err != nil {
|
||||
log.Fatal("Unable to send the response data: ", err)
|
||||
}
|
||||
}
|
|
@ -0,0 +1,15 @@
|
|||
package version
|
||||
|
||||
import "fmt"
|
||||
|
||||
const major = 3
|
||||
const minor = 0
|
||||
const patch = 0
|
||||
|
||||
// Code version as integer
|
||||
const Code = major*1000000 + minor*1000 + patch
|
||||
|
||||
// String version as string
|
||||
func String() string {
|
||||
return fmt.Sprintf("%d.%d.%d", major, minor, patch)
|
||||
}
|
Loading…
Reference in New Issue