Skip to content

Commit

Permalink
Add Control Plane API call to change username and password for basic …
Browse files Browse the repository at this point in the history
…HTTP authentication (closes #117)
  • Loading branch information
aldemirenes authored and BenFradet committed Dec 12, 2017
1 parent 7c840ad commit a4dc77c
Show file tree
Hide file tree
Showing 5 changed files with 270 additions and 0 deletions.
43 changes: 43 additions & 0 deletions provisioning/resources/control-plane/change_credentials.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Copyright (c) 2016-2017 Snowplow Analytics Ltd.
* All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache
* License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied.
*
* See the Apache License Version 2.0 for the specific language
* governing permissions and limitations there under.
*/

package main

import (
"io/ioutil"
"strings"
)

func changeCredentials(configPath string, username string, password string) error {
lines, err := fileToLines(configPath)
if err != nil {
return err
}

fileContent := ""
for _, line := range lines {
if strings.Contains(line, "basicauth") {
line = " basicauth " + username + " " + password + " {"
}
fileContent += line
fileContent += "\n"
}

return ioutil.WriteFile(configPath, []byte(fileContent), 0644)
}
76 changes: 76 additions & 0 deletions provisioning/resources/control-plane/change_credentials_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* Copyright (c) 2016-2017 Snowplow Analytics Ltd.
* All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache
* License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at
* http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed
* on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
* either express or implied.
*
* See the Apache License Version 2.0 for the specific language
* governing permissions and limitations there under.
*/

package main

import (
"github.com/stretchr/testify/assert"
"io/ioutil"
"os"
"path/filepath"
"testing"
)

func TestChangeCredentials(t *testing.T) {
assert := assert.New(t)

caddyConfigHeadBefore :=
`*:80 {
tls off
basicauth "USERNAME_PLACEHOLDER" PASSWORD_PLACEHOLDER {
/home
/kibana
/elasticsearch
/control-plane
/_plugin
}
`
expectedCaddyConfigHeadAfter :=
`*:80 {
tls off
basicauth username_test password_test {
/home
/kibana
/elasticsearch
/control-plane
/_plugin
}
`
dir, err := ioutil.TempDir("", "testDir")
assert.Nil(err)

defer os.RemoveAll(dir)

tmpfn := filepath.Join(dir, "tmpfile")

err = ioutil.WriteFile(tmpfn, []byte(caddyConfigHeadBefore), 0666)
assert.Nil(err)

err = changeCredentials(
tmpfn,
"username_test",
"password_test",
)
assert.Nil(err)

caddyConfigAfter, err := ioutil.ReadFile(tmpfn)
assert.Nil(err)

assert.True(expectedCaddyConfigHeadAfter == string(caddyConfigAfter))
}
37 changes: 37 additions & 0 deletions provisioning/resources/control-plane/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ func main() {
http.HandleFunc("/enrichments", uploadEnrichments)
http.HandleFunc("/external-iglu", addExternalIgluServer)
http.HandleFunc("/local-iglu-apikey", addLocalIgluApikey)
http.HandleFunc("/credentials", changeUsernameAndPassword)
log.Fatal(http.ListenAndServe(":10000", nil))
}

Expand Down Expand Up @@ -219,3 +220,39 @@ func addLocalIgluApikey(resp http.ResponseWriter, req *http.Request) {
http.Error(resp, "", 404)
}
}

func changeUsernameAndPassword(resp http.ResponseWriter, req *http.Request) {
if req.Method == "POST" {
req.ParseForm()

newUsernameArr, checkUsername := req.Form["new_username"]
newPasswordArr, checkPassword := req.Form["new_password"]
if !(checkUsername && checkPassword) {
http.Error(resp, "missing parameter", 400)
return
}
newUsername := newUsernameArr[0]
newPassword := newPasswordArr[0]

err := changeCredentials(
config.Dirs.Config+"/"+config.ConfigNames.Caddy,
newUsername,
newPassword,
)
if err != nil {
http.Error(resp, err.Error(), 500)
return
}

err = restartService("caddy")
if err != nil {
http.Error(resp, err.Error(), 500)
return
}

resp.WriteHeader(http.StatusOK)
io.WriteString(resp, "changed successfully")
} else {
http.Error(resp, "", 404)
}
}
2 changes: 2 additions & 0 deletions provisioning/resources/ui/js/components/ControlPlane.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import RestartServicesSection from "./ControlPlaneComponents/RestartServices";
import UploadEnrichmentsForm from "./ControlPlaneComponents/UploadEnrichments";
import AddExternalIgluServerForm from "./ControlPlaneComponents/AddExternalIgluServer";
import AddLocalIgluApikeyForm from "./ControlPlaneComponents/AddLocalIgluApikey";
import ChangeUsernamePasswordForm from "./ControlPlaneComponents/ChangeUsernamePassword";

export class ControlPlane extends React.Component<{}, {}> {

Expand All @@ -34,6 +35,7 @@ export class ControlPlane extends React.Component<{}, {}> {
<UploadEnrichmentsForm />
<AddExternalIgluServerForm />
<AddLocalIgluApikeyForm />
<ChangeUsernamePasswordForm />
</div>
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/*
* Copyright (c) 2016-2017 Snowplow Analytics Ltd. All rights reserved.
*
* This program is licensed to you under the Apache License Version 2.0,
* and you may not use this file except in compliance with the Apache License Version 2.0.
* You may obtain a copy of the Apache License Version 2.0 at http://www.apache.org/licenses/LICENSE-2.0.
*
* Unless required by applicable law or agreed to in writing,
* software distributed under the Apache License Version 2.0 is distributed on an
* "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the Apache License Version 2.0 for the specific language governing permissions and limitations there under.
*/

/// <reference path="../../../typings/node/node.d.ts" />
/// <reference path="../../../typings/react/react.d.ts" />
/// <reference path="../../../typings/react/react-dom.d.ts" />
/// <reference path="../.././Interfaces.d.ts"/>

import React = require('react');
import ReactDOM = require("react-dom");
import AlertContainer from 'react-alert';
import alertOptions from './AlertOptions'
import axios from 'axios';

var alertContainer = new AlertContainer();

export default React.createClass({
getInitialState () {
return {
new_username: '',
new_password: '',
disabled: false
};
},

handleChange(evt) {
if (evt.target.name == 'new_username'){
this.setState({
new_username: evt.target.value
});
}
else if (evt.target.name == 'new_password'){
this.setState({
new_password: evt.target.value
});
}
},

sendFormData() {
var alertShow = alertContainer.show
var _this = this

// there is no need to make 'disabled' false after
// because connection will be lost after request is sent
// and page must be loaded again
_this.setState({
disabled: true
});

var params = new URLSearchParams();
params.append('new_username', this.state.new_username)
params.append('new_password', this.state.new_password)

var _this = this
axios.defaults.headers.post['Content-Type'] = 'application/x-www-form-urlencoded'
axios.post('/control-plane/credentials', params, {})
.then(function (response) {
// there is no need to this part because status will be
// 400 in everytime and this will be handled by catch section
})
.catch(function (error) {
alertShow("You will lose connection after change the username and \
password because of server restarting. Reload the page \
after submission and login with your new username and password.", {
time: 10000,
type: 'info'
});
});
},

handleSubmit(event) {
var alertShow = alertContainer.show
alertShow('Please wait...', {
time: 2000,
type: 'info'
});
event.preventDefault();
this.sendFormData();
},

render() {
return (
<div className="tab-content">
<h4>Change username and password for basic http authentication: </h4>
<form action="" onSubmit={this.handleSubmit}>
<div className="form-group">
<label htmlFor="new_username">Username: </label>
<input className="form-control" name="new_username" ref="new_username" required type="text" onChange={this.handleChange} value={this.state.new_username} />
</div>
<div className="form-group">
<label htmlFor="new_password">Password: </label>
<input className="form-control" name="new_password" ref="new_password" required type="password" onChange={this.handleChange} value={this.state.new_password} />
</div>
<div className="form-group">
<button className="btn btn-primary" type="submit" disabled={this.state.disabled}>Submit</button>
</div>
</form>
<AlertContainer ref={a => alertContainer = a} {...alertOptions} />
</div>
);
}
});

0 comments on commit a4dc77c

Please sign in to comment.