Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

acra_configui #111

Merged
merged 7 commits into from
Mar 5, 2018
Merged
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
185 changes: 185 additions & 0 deletions cmd/acra_configui/acra_configui.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,185 @@
package main

// TODO: errors output,

// sdfsdf
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

?)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cleaned


import (
//"io/ioutil"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we remove this lines? https://godoc.org/golang.org/x/tools/cmd/goimports - this tool drop unnecessary imports and add missing. it help to not care about commenting some imports. (golang IDE can run it for every .go file on save)

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cleaned. Tip: IDE can manage this using "Reformat Code" action

//"html/template"
"net/http"
"log"
"html/template"
"path/filepath"
"gopkg.in/yaml.v2"
"io/ioutil"
"time"
"encoding/json"
"strconv"
"bytes"
"os"
"fmt"
"flag"
)

func check(e error) {
if e != nil {
panic(e)
}
}

type paramYAML struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I thought how we can avoid duplicating and remembered that we can operate with interface{} or map values and convert to/from json/yaml. But when I tried use struct directly I took correct converting without hacks

package main

import (
	"fmt"
	"gopkg.in/yaml.v2"
	"encoding/json"
)

type SomeStruct struct{
	A string
	B bool
	C int
	D []string
}

func main(){
	var a SomeStruct
        // var a map[string]interface{} // it works too
	if err := json.Unmarshal([]byte(`{"A": "str", "B": true, "C": 123, "D": ["a1", "a2", "a3"]}`), &a); err != nil{
		panic(err)
	}
	fmt.Println(a)
	out, err := yaml.Marshal(a)
	if err != nil{
		panic(err)
	}
	fmt.Println(string(out))
}

So I think better to use one struct instead two and avoid problem with synchronization

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It fails for me in go1.5.4 with struct tags to make input/output more json/yaml convenient and compatible to current configs

Name string `yaml:"name"`
Title string `yaml:"title"`
Value_type string `yaml:"value_type"`
Input_type string `yaml:"input_type"`
Values []string `yaml:"values,flow"`
Labels []string `yaml:"labels,flow"`
}

type paramJSON struct {
Name string `json:"name"`
Title string `json:"title"`
Value_type string `json:"value_type"`
Input_type string `json:"input_type"`
Values []string `json:"values,flow"`
Labels []string `json:"labels,flow"`
}

type configParamsYAML struct {
Config []paramYAML
}

type configParamsJSON struct {
Config []paramJSON
}

type ConfigAcraServer struct {
ProxyHost string `json:"host"`
ProxyPort int `json:"port"`
DbHost string `json:"db_host"`
DbPort int `json:"db_port"`
ProxyCommandsPort int `json:"commands_port"`
Debug bool `json:"debug"`
ScriptOnPoison string `json:"poisonscript"`
StopOnPoison bool `json:"poisonshutdown"`
WithZone bool `json:"zonemode"`
}

type JsonResponse struct {
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Where is this struct used?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed

Success bool `json:"success"`
}

func nop_map(map[string][]string) {}
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is it used anywhere?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nop, removed

func nop_string(string) {}

func SubmitSettings(w http.ResponseWriter, r *http.Request) {
if r.Method != "POST" {
http.Error(w, "Invalid request method", http.StatusMethodNotAllowed)
return
}

err := r.ParseForm()
if err != nil {
panic(err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think better to log error and exit. Then we can collect logs and do some processing, count errors, etc.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agree to log, but it's request processing and we should continue with return

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we can panic and log together. panic to process error as it expected by framework and add log record to process it in a future with our format, not with format of panic messages. Human message like "can't parse post form in 'getConfig' api methid" will be better than something like "incorrect url parameters format"

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
var db_port, _ = strconv.Atoi(r.Form.Get("db_port"))
var commands_port, _ = strconv.Atoi(r.Form.Get("commands_port"))
var debug, _ = strconv.ParseBool(r.Form.Get("debug"))
var zonemode, _ = strconv.ParseBool(r.Form.Get("zonemode"))
var poisonshutdown, _ = strconv.ParseBool(r.Form.Get("poisonshutdown"))
config := ConfigAcraServer{
DbHost: r.Form.Get("db_host"),
DbPort: db_port,
ProxyCommandsPort: commands_port,
Debug: debug,
ScriptOnPoison: r.Form.Get("poisonscript"),
StopOnPoison: poisonshutdown,
WithZone: zonemode,
}
jsonToServer, err := json.Marshal(config)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
req, err := http.NewRequest("POST", "http://localhost:9292/setConfig", bytes.NewBuffer(jsonToServer))
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

better to change hardcoded string in code to some const or read from config
plus why didn't checked error err != nil?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

req.Header.Set("Content-Type", "application/json")
client := &http.Client{}
resp, err := client.Do(req)
if err != nil {
http.Error(w, err.Error(), http.StatusInternalServerError)
return
}
defer resp.Body.Close()
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

code below can't fail (as I see, they don't return any error) so better to just call resp.Body.Close at end. defer has little overhead in runtime.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done


w.Header().Set("Content-Type", "application/json")
w.Write(jsonToServer)
}

func index(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Security-Policy", "require-sri-for script style")
ip := filepath.Join("static", "index.html")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what about ip -> templatePath or just `template.ParseFiles(filepath.Join("static", "index.html"))?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

refactored

tmpl, _ := template.ParseFiles(ip)
var outConfigParams configParamsYAML
configParamsYAML, err := ioutil.ReadFile("acraserver_config_vars.yaml")
check(err)

// get current config
var netClient = &http.Client{
Timeout: time.Second * 5,
}
serverResponse, err := netClient.Get("http://localhost:9292/getConfig")
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the same as above with hardcoded url

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed

if err != nil {
log.Printf("ERROR: api error - %s", err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

in previous PR I changed all logs to using github.com/sirupsen/logrus. Can you use log.Errorf instead? This new module has helper like log.WithError(err).Errorln("api error") that helps log err and custom message

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Changed to github.com/sirupsen/logrus

}
serverConfigDataJsonString, err := ioutil.ReadAll(serverResponse.Body)
defer serverResponse.Body.Close()
if err != nil {
log.Fatal(err)
}
var serverConfigData ConfigAcraServer
err = json.Unmarshal(serverConfigDataJsonString, &serverConfigData)
if err != nil {
log.Printf("ERROR: json.Unmarshal error - %s", err)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same about logrus package

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done

}
// log.Println(serverConfigData)
// end get current config

yaml.Unmarshal(configParamsYAML, &outConfigParams)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

checked


var configParams configParamsJSON
for _, item := range outConfigParams.Config {
c := paramJSON{
Name: item.Name,
Title: item.Title,
Value_type: item.Value_type,
Input_type: item.Input_type,
Values: item.Values,
Labels: item.Labels,
}
configParams.Config = append(configParams.Config, c)
}
// log.Println(configParams)

res, err := json.Marshal(configParams)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

check error?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed


tmpl.Execute(w, struct {
ConfigParams string
ConfigAcraServer
}{
string(res),
serverConfigData,
})
}

func main() {
port := flag.Int("port", 8000, "Port for configUI HTTP endpoint")
flag.Parse()
http.HandleFunc("/index.html", index)
http.HandleFunc("/", index)
http.Handle("/static/", http.StripPrefix("/static/", http.FileServer(http.Dir("./static"))))
http.HandleFunc("/acraserver/submit_setting", SubmitSettings)
log.Println(fmt.Sprintf("AcraConfigUI is listening @ :%d with PID %d", *port, os.Getpid()))
err := http.ListenAndServe(fmt.Sprintf(":%d", *port), nil)
check(err)
}
58 changes: 58 additions & 0 deletions cmd/acra_configui/acraserver_config_vars.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
config:
# -
# name: host
# title: Host network address to listen for incoming connections from AcraProxy or via SSL
# value_type: string
# input_type: text
# -
# name: port
# title: Port for AcraServer to listen for incoming connections from AcraProxy or via SSL
# value_type: int8
# input_type: number
-
name: db_host
title: Host for destination Postgres
value_type: string
input_type: text
-
name: db_port
title: Port for destination Postgres
value_type: int8
input_type: number
-
name: commands_port
title: Port for AcraServer's HTTP API
value_type: int8
input_type: number
-
name: debug
title: Turn on debug logging
value_type: bool
input_type: radio
values: [true, false]
labels: [Yes, No]
-
name: poisonscript
title: Execute script on detecting poison record
value_type: string
input_type: text
-
name: poisonshutdown
title: Stop on detecting poison record
value_type: bool
values: [true, false]
labels: [Yes, No]
input_type: radio

-
name: server_id
title: ID to be sent in secure session
value_type: string
input_type: text
-
name: zonemode
title: Turn on zone mode
value_type: bool
values: [true, false]
labels: [Yes, No]
input_type: radio
Binary file added cmd/acra_configui/static/img/favicon.ico
Binary file not shown.
99 changes: 99 additions & 0 deletions cmd/acra_configui/static/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<meta charset="utf-8">
<title>ACRA ConfigUI</title>
<link rel="shortcut icon" type="image/x-icon" href="/static/img/favicon.ico"/>

<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet"
integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">

<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

can we place blocking javascript resources before </body>? there used external resources that can be slow or unreachable

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need this to use jQuery and see rendered layout with styles applied.
We may move them inside out src later.

integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.bundle.min.js"
integrity="sha384-feJI7QwhOS+hwpX2zkaeJQjeiwlhOP+SdQDqhgvvo1DsjtiSQByFdThsxO669S2D" crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jsrender/0.9.90/jsrender.js"
integrity="sha256-vnIot7uxN4jRvM5P1QDlpy3M8eiR0KwODT0QFKHDhq8=" crossorigin="anonymous"></script>
<script type="text/javascript" src="/static/js/main.js"></script>
<script type="text/javascript">
configParams = JSON.parse({{.ConfigParams}});
currentConfig = {{.ConfigAcraServer}};
</script>

<style type="text/css">
html {
font-size: 0.9rem;
}
body{
padding: 1em;
}
.template{
display: none;
}
</style>

<script id="settingsTplRow" type="text/x-jsrender" class="template">
<table class="table">
<thead>
<tr>
<th scope="col">#</th>
<th scope="col">Value</th>
<th scope="col">Description</th>
<th scope="col">Alias</th>
</tr>
</thead>
<tbody>
{-for options itemVar="~options"-}
<tr>
<th scope="row">{-:#getIndex()+1-}</th>
<td>
{-if input_type == 'number'-}
<input type="number" name="{-:name-}" value="">
{-else input_type == 'radio'-}
{-for labels-}
<input type="radio" name="{-:~options.name-}" value="{-:~options.values[#index]-}" id="{-:~options.name-}_{->#data-}">
<label for="{-:~options.name-}_{->#data-}">{->#data-}</label>
{-/for-}
{-else-}
<input type="text" name="{-:name-}" value="">
{-/if-}
</td>
<td>{-:title-}</td>
<td>{-:name-}</td>
</tr>
{-/for-}
</tbody>
</table>
<button onclick="javascript: save();">Save</button>
</script>

</head>

<body>

<div class="row">
<div class="col-3">
<div class="nav flex-column nav-pills" id="v-pills-tab" role="tablist" aria-orientation="vertical">
<a class="nav-link active show" id="v-pills-settings-tab" data-toggle="pill" href="#v-pills-settings" role="tab" aria-controls="v-pills-settings" aria-selected="false">AcraServer Settings</a>
<a class="nav-link" id="v-pills-profile-tab" data-toggle="pill" href="#v-pills-profile" role="tab" aria-controls="v-pills-profile" aria-selected="true">Intrusion detection</a>
<a class="nav-link" id="v-pills-messages-tab" data-toggle="pill" href="#v-pills-messages" role="tab" aria-controls="v-pills-messages" aria-selected="false">Zones</a>
</div>
</div>
<div class="col-9">
<div class="tab-content" id="v-pills-tabContent">
<div class="tab-pane fade active show" id="v-pills-settings" role="tabpanel" aria-labelledby="v-pills-settings-tab">

</div>
<div class="tab-pane fade" id="v-pills-profile" role="tabpanel" aria-labelledby="v-pills-profile-tab">
<p>Coming soon...</p>
</div>
<div class="tab-pane fade" id="v-pills-messages" role="tabpanel" aria-labelledby="v-pills-messages-tab">
<p>Coming soon...</p>
</div>
</div>
</div>
</div>

</body>
</html>
52 changes: 52 additions & 0 deletions cmd/acra_configui/static/js/main.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
$(document).ready(function () {
$.views.settings.delimiters("{-", "-}");
var options = [];
$.each(configParams.Config, function (i, item) {
options.push(item);
});
var tpl = $($.templates('#settingsTplRow').render({
options: options
}));
tpl.appendTo($('#v-pills-settings'));

// set checkox values
$.each(configParams['Config'], function (i, item) {
if (item.input_type == 'radio') {
if (currentConfig[item.name] == undefined) {
$('#v-pills-settings').find('input[type="radio"][name="' + item.name + '"][value="' + item.value + '"]').attr('checked', 'checked');
}
else {
var v = currentConfig[item.name] ? 1 : 0;
$('#v-pills-settings').find('input[type="radio"][name="' + item.name + '"][value="' + currentConfig[item.name] + '"]').attr('checked', 'checked');
}
}
else {
$('#v-pills-settings').find('input[name="' + item.name + '"]').val(currentConfig[item.name]);
}
});

$('#v-pills-tab a').on('click', function (e) {
e.preventDefault();
$(this).tab('show');
})
});

var save = function () {
var data = {};
$.each(configParams['Config'], function (i, item) {
if (item.input_type == 'radio') {
data[item.name] = $('#v-pills-settings').find('input:checked[type="radio"][name="' + item.name + '"]').val();
}
else {
data[item.name] = $('#v-pills-settings').find('input[name="' + item.name + '"]').val();
}
});

$.ajax({
method: 'POST',
url: "/acraserver/submit_setting",
data: data
}).done(function () {
$(this).addClass("done");
});
};
Loading