Update documentation for TLS and authentication
Change-Id: Iac283b4feb17b5a9ddaf4d50d4fa53b77d29a4a3
This commit is contained in:
parent
172be5b722
commit
f59f2bc6eb
10
Makefile
10
Makefile
@ -47,19 +47,11 @@ GO_FLAGS := -ldflags=$(LD_FLAGS) -trimpath
|
|||||||
BUILD_DIR := bin
|
BUILD_DIR := bin
|
||||||
|
|
||||||
# Find all main.go files under cmd, excluding airshipui itself
|
# Find all main.go files under cmd, excluding airshipui itself
|
||||||
EXAMPLE_NAMES := $(notdir $(subst /main.go,,$(wildcard examples/*/main.go)))
|
|
||||||
EXAMPLES := $(addprefix $(BUILD_DIR)/, $(EXAMPLE_NAMES))
|
|
||||||
MAIN := $(BUILD_DIR)/airshipui
|
MAIN := $(BUILD_DIR)/airshipui
|
||||||
EXTENSION :=
|
EXTENSION :=
|
||||||
|
|
||||||
ifdef XDG_CONFIG_HOME
|
ifeq ($(OS),Windows_NT)
|
||||||
OCTANT_PLUGINSTUB_DIR ?= ${XDG_CONFIG_HOME}/octant/plugins
|
|
||||||
# Determine if on windows
|
|
||||||
else ifeq ($(OS),Windows_NT)
|
|
||||||
OCTANT_PLUGINSTUB_DIR ?= $(subst \,/,${LOCALAPPDATA}/octant/plugins)
|
|
||||||
EXTENSION=.exe
|
EXTENSION=.exe
|
||||||
else
|
|
||||||
OCTANT_PLUGINSTUB_DIR ?= ${HOME}/.config/octant/plugins
|
|
||||||
endif
|
endif
|
||||||
|
|
||||||
DIRS = internal
|
DIRS = internal
|
||||||
|
@ -15,12 +15,14 @@ to go to a separate url or application.
|
|||||||
git clone https://opendev.org/airship/airshipui
|
git clone https://opendev.org/airship/airshipui
|
||||||
cd airshipui
|
cd airshipui
|
||||||
make # Note running behind a proxy can cause issues, notes on solving is in the Appendix of the Developer's Guide
|
make # Note running behind a proxy can cause issues, notes on solving is in the Appendix of the Developer's Guide
|
||||||
|
bin/airshipui
|
||||||
```
|
```
|
||||||
|
Once AirshipUI has started you should be able to browse to it at https://localhost:10443
|
||||||
|
|
||||||
## Adding Additional Functionality
|
## Adding Additional Functionality
|
||||||
|
|
||||||
Airship UI can be seamlessly integrated with service dashboards and other web-based tools by providing the necessary
|
Airship UI can be seamlessly integrated with service dashboards and other web-based tools by providing the necessary
|
||||||
configuration in $HOME/.airship/airshipui.json.
|
configuration in etc/airshipui.json.
|
||||||
|
|
||||||
To add service dashboards, create a section at the top-level of airshipui.json as follows:
|
To add service dashboards, create a section at the top-level of airshipui.json as follows:
|
||||||
|
|
||||||
|
BIN
docs/img/authentication.jpg
Executable file
BIN
docs/img/authentication.jpg
Executable file
Binary file not shown.
After Width: | Height: | Size: 13 KiB |
@ -10,7 +10,6 @@ Clone the Airship UI repository and build.
|
|||||||
git clone https://opendev.org/airship/airshipui
|
git clone https://opendev.org/airship/airshipui
|
||||||
cd airshipui
|
cd airshipui
|
||||||
make # Note running behind a proxy can cause issues, notes on solving is in the Appendix
|
make # Note running behind a proxy can cause issues, notes on solving is in the Appendix
|
||||||
make examples # (optional)
|
|
||||||
|
|
||||||
**NOTE:** Make will install node.js-v12.16.3 into your tools directory and will use that as the node binary for the UI
|
**NOTE:** Make will install node.js-v12.16.3 into your tools directory and will use that as the node binary for the UI
|
||||||
building, testing and linting. For windows this can be done using [cygwin](https://www.cygwin.com/) make.
|
building, testing and linting. For windows this can be done using [cygwin](https://www.cygwin.com/) make.
|
||||||
@ -19,41 +18,51 @@ Run the airshipui binary
|
|||||||
|
|
||||||
./bin/airshipui
|
./bin/airshipui
|
||||||
|
|
||||||
## Authentication
|
## Security
|
||||||
|
|
||||||
### Pluggable authentication methods
|
### Transport Layer Security
|
||||||
The AirshipUI is not designed to create authentication credentials but to have them supplied to it either by a
|
The UI will need to send sensitive / receive information therefore all channels of communication will need to be encrypted. The main protocol for this is [HTTPS](https://en.wikipedia.org/wiki/HTTPS), and the websocket communication is also over the secured channel ([wss](https://tools.ietf.org/html/rfc6455#page-55))
|
||||||
configuration or by an external entity. The expectation is that there will be an external URL that will handle
|
|
||||||
authentication for the system which may need to be modified or created. The endpoint will need to be able to
|
|
||||||
forward a
|
|
||||||
[bearer token](https://oauth.net/2/bearer-tokens/),
|
|
||||||
[basic auth](https://en.wikipedia.org/wiki/Basic_access_authentication)
|
|
||||||
or cookie data to the Airship UI backend service.
|
|
||||||
|
|
||||||
To configure the pluggable authentication the following must be added to the $HOME/.airshipui/airshipui.json file:
|
The airshipui stores the public and private key location in the etc/airshipui.json by default. If one is not present at the time of the airshipui is started a self signed certificate and private key will be generated and stored in etc/airshipui.json for the server to start in a developer's mode. This will cause an SSL error on your browser that
|
||||||
|
you will need to click past to get to the UI. It is assumed the server will have access to the proper key & certificate in production. Both the private key and certificate need to be ASCII PEM formatted.
|
||||||
|
|
||||||
"authMethod": {
|
Example webservice definition in etc/airshipui.json:
|
||||||
"url": "<protocol>://<host:port>/<path>/<method>"
|
```
|
||||||
|
"webservice": {
|
||||||
|
"host": "<host, default is localhost>",
|
||||||
|
"port": <port, default is 10443>,
|
||||||
|
"publicKey": "<path>/<cert>.pem",
|
||||||
|
"privateKey": "<path>/<private_key>.pem"
|
||||||
|
},
|
||||||
|
```
|
||||||
|
### User Authentication
|
||||||
|
The UI uses Json Web Tokens ([JWT](https://tools.ietf.org/html/rfc7519)) to control access to the UI. The default method of generation is based on a userid and password enforcement
|
||||||
|
that the user is required to enter the first time accessing the UI. The UI will store the token locally and use it to authenticate the communication with the backend on every
|
||||||
|
transaction.
|
||||||
|
|
||||||
|
The airshipui stores the user and password in etc/airshipui.json by default. The userid is clear text but the password is an non reversible sha512 hash of the password which is used to compare the supplied password with the expected password. No clear text passwords are stored.
|
||||||
|
|
||||||
|
If no id and password is supplied the airshipui will create a default userid and password and store it in the etc/airshipui.json file so there will be no ability to use the UI
|
||||||
|
without a base id / password challenge authentication.
|
||||||
|
|
||||||
|
The default userid is: admin The default password is: admin
|
||||||
|
|
||||||
|
To generate a password you can run the password.go program in the tools directory:
|
||||||
|
```
|
||||||
|
$ go run tools/password.go test_password
|
||||||
|
c8afeec4e9d29fa6307bc246965fe136a95bc47a9cfdedba0df256358eaa45ec0bf8d7a4333a4b13dc9a5508137d0f4d212272b27e64e41d4745a66b5f480759
|
||||||
|
```
|
||||||
|
|
||||||
|
Example user definition in etc/airshipui.json:
|
||||||
|
```
|
||||||
|
"users": {
|
||||||
|
"test": "c8afeec4e9d29fa6307bc246965fe136a95bc47a9cfdedba0df256358eaa45ec0bf8d7a4333a4b13dc9a5508137d0f4d212272b27e64e41d4745a66b5f480759"
|
||||||
}
|
}
|
||||||
|
```
|
||||||
|
After the user is defined in the etc/airshipui.json file the user can be used for authentication going forward.
|
||||||
|
|
||||||
Note: By default the system will start correctly without any authentication urls supplied to the configuration.
|
### Authentication decision tree
|
||||||
The expectation is that AirshipUI will be running in a minimal least authorized configuration.
|
![AirshipUI Interactions](../img/authentication.jpg "AirshipUI Authentication Decision Tree")
|
||||||
|
|
||||||
### Example Auth Server
|
|
||||||
There is an example authentication server in examples/authentication/main.go. These endpoints can be added to the
|
|
||||||
$HOME/.airshipui/airshipui.json and will allow the system to show a basic authentication test.
|
|
||||||
1. Basic auth on http://127.0.0.1:12321/basic-auth
|
|
||||||
2. Cookie based auth on http://127.0.0.1:12321/cookie
|
|
||||||
3. OAuth JWT (JSON Web Token) on http://127.0.0.1:12321/oauth
|
|
||||||
|
|
||||||
To start the system cd to the root of the AirshipUI repository and execute:
|
|
||||||
|
|
||||||
go run examples/authentication/main.go
|
|
||||||
|
|
||||||
#### Example Auth Server Credentials
|
|
||||||
+ The example auth server id is: **airshipui**
|
|
||||||
+ The example auth server password is: **Open Sesame!**
|
|
||||||
|
|
||||||
|
|
||||||
## Behind the scenes
|
## Behind the scenes
|
||||||
|
|
||||||
|
@ -1,262 +0,0 @@
|
|||||||
/*
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
|
||||||
you may not use this file except in compliance with the License.
|
|
||||||
You may obtain a copy of the License at
|
|
||||||
|
|
||||||
https://www.apache.org/licenses/LICENSE-2.0
|
|
||||||
|
|
||||||
Unless required by applicable law or agreed to in writing, software
|
|
||||||
distributed under the License is distributed on an "AS IS" BASIS,
|
|
||||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
||||||
See the License for the specific language governing permissions and
|
|
||||||
limitations under the License.
|
|
||||||
*/
|
|
||||||
|
|
||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"crypto/aes"
|
|
||||||
"crypto/cipher"
|
|
||||||
"crypto/rand"
|
|
||||||
"encoding/base64"
|
|
||||||
"encoding/json"
|
|
||||||
"io"
|
|
||||||
"io/ioutil"
|
|
||||||
"log"
|
|
||||||
"net/http"
|
|
||||||
"text/template"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/dgrijalva/jwt-go"
|
|
||||||
)
|
|
||||||
|
|
||||||
// page struct is used for templated HTML
|
|
||||||
type page struct {
|
|
||||||
Title string
|
|
||||||
}
|
|
||||||
|
|
||||||
// id and password passed from the test page
|
|
||||||
type authRequest struct {
|
|
||||||
ID string `json:"id,omitempty"`
|
|
||||||
Password string `json:"password,omitempty"`
|
|
||||||
}
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
// we're not picky, so we'll take everything and sort it out later
|
|
||||||
http.HandleFunc("/", handler)
|
|
||||||
|
|
||||||
log.Println("Example Auth Server listening on :12321")
|
|
||||||
err := http.ListenAndServe(":12321", nil)
|
|
||||||
if err != nil {
|
|
||||||
log.Fatal(err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// URI check for /basic-auth, /cookie and /oauth, everything else gets a 404
|
|
||||||
// Also a switch for GET and POST, everything else gets a 415
|
|
||||||
func handler(w http.ResponseWriter, r *http.Request) {
|
|
||||||
method := r.Method
|
|
||||||
|
|
||||||
uri := r.RequestURI
|
|
||||||
if uri == "/basic-auth" || uri == "/cookie" || uri == "/oauth" {
|
|
||||||
switch method {
|
|
||||||
case http.MethodGet:
|
|
||||||
get(uri, w)
|
|
||||||
case http.MethodPost:
|
|
||||||
post(uri, w, r)
|
|
||||||
default:
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
log.Printf("Method %s for %s being rejected, not implemented", method, uri)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
w.WriteHeader(http.StatusNotFound)
|
|
||||||
log.Printf("URI %s being rejected, not found", uri)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle the GET function and return a templated page
|
|
||||||
func get(uri string, w http.ResponseWriter) {
|
|
||||||
var p page
|
|
||||||
|
|
||||||
switch uri {
|
|
||||||
case "/basic-auth":
|
|
||||||
p = page{
|
|
||||||
Title: "Basic Auth",
|
|
||||||
}
|
|
||||||
case "/cookie":
|
|
||||||
p = page{
|
|
||||||
Title: "Cookie",
|
|
||||||
}
|
|
||||||
case "/oauth":
|
|
||||||
p = page{
|
|
||||||
Title: "OAuth",
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if p != (page{}) {
|
|
||||||
// parse and merge the template
|
|
||||||
err := template.Must(template.ParseFiles("./examples/authentication/templates/index.html")).Execute(w, p)
|
|
||||||
if err != nil {
|
|
||||||
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
|
|
||||||
log.Printf("Error getting the templated html: %v", err)
|
|
||||||
http.Error(w, "Error getting the templated html", http.StatusInternalServerError)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// handle the POST function and return a mock authentication
|
|
||||||
func post(uri string, w http.ResponseWriter, r *http.Request) {
|
|
||||||
body, err := ioutil.ReadAll(r.Body)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error reading body: %v", err)
|
|
||||||
http.Error(w, "can't read body", http.StatusBadRequest)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
var authAttempt authRequest
|
|
||||||
err = json.Unmarshal(body, &authAttempt)
|
|
||||||
|
|
||||||
if err == nil {
|
|
||||||
// TODO: make the id and password part of a conf file somewhere
|
|
||||||
id := authAttempt.ID
|
|
||||||
passwd := authAttempt.Password
|
|
||||||
if id == "airshipui" && passwd == "Open Sesame!" {
|
|
||||||
w.WriteHeader(http.StatusCreated)
|
|
||||||
|
|
||||||
response := map[string]interface{}{
|
|
||||||
"id": id,
|
|
||||||
"name": "Some Name",
|
|
||||||
"expiration": time.Now().Add(time.Hour * 24).Unix(),
|
|
||||||
}
|
|
||||||
|
|
||||||
switch uri {
|
|
||||||
case "/basic-auth":
|
|
||||||
response["X-Auth-Token"] = base64.StdEncoding.EncodeToString([]byte(id + ":" + passwd))
|
|
||||||
response["type"] = "basic-auth"
|
|
||||||
postHelper(response, w)
|
|
||||||
case "/cookie":
|
|
||||||
response["type"] = "cookie"
|
|
||||||
cookieHandler(response, w)
|
|
||||||
case "/oauth":
|
|
||||||
response["type"] = "oauth"
|
|
||||||
jwtHandler(id, passwd, response, w)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
|
|
||||||
http.Error(w, "Bad id or password", http.StatusUnauthorized)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
w.Header().Set("Content-Type", "text/plain; charset=UTF-8")
|
|
||||||
log.Printf("Error unmarshalling the request: %v", err)
|
|
||||||
http.Error(w, "Error unmarshalling the request", http.StatusBadRequest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// potentially more complex logic happens here with cookie data
|
|
||||||
func cookieHandler(response map[string]interface{}, w http.ResponseWriter) {
|
|
||||||
cookie, err := json.Marshal(response)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error marshaling cookie response: %v", err)
|
|
||||||
}
|
|
||||||
b, err := encrypt(cookie)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error encrypting cookie response: %v", err)
|
|
||||||
postHelper(nil, w)
|
|
||||||
} else {
|
|
||||||
response["cookie"] = b
|
|
||||||
postHelper(response, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// potentially more complex logic happens here with JWT data
|
|
||||||
func jwtHandler(id string, passwd string, response map[string]interface{}, w http.ResponseWriter) {
|
|
||||||
token, err := createToken(id, passwd)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error creating JWT token: %v", err)
|
|
||||||
postHelper(nil, w)
|
|
||||||
} else {
|
|
||||||
response["jwt"] = token
|
|
||||||
postHelper(response, w)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Helper function to reduce the number of error checks that have to happen in other functions
|
|
||||||
func postHelper(returnData map[string]interface{}, w http.ResponseWriter) {
|
|
||||||
if returnData == nil {
|
|
||||||
http.Error(w, "Internal error", http.StatusInternalServerError)
|
|
||||||
} else {
|
|
||||||
log.Printf("Auth data %s\n", returnData)
|
|
||||||
b, err := json.Marshal(returnData)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error marshaling the response: %v", err)
|
|
||||||
http.Error(w, "Internal error", http.StatusInternalServerError)
|
|
||||||
} else {
|
|
||||||
_, err := w.Write(b)
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error sending POST response to client: %v", err)
|
|
||||||
} else {
|
|
||||||
go notifyElectron(b)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// This is intended to send an auth completed message to the system so that it knows there was a successful login
|
|
||||||
func notifyElectron(data []byte) {
|
|
||||||
// TODO: probably need to pull the electron url out into its own
|
|
||||||
resp, err := http.Post("http://localhost:8080/auth", "application/json; charset=UTF-8", bytes.NewBuffer(data))
|
|
||||||
if err != nil {
|
|
||||||
log.Printf("Error sending auth complete to electron. The response is %v, the error is %v\n", resp, err)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// aes requires a 32 byte key, this is random for demo purposes
|
|
||||||
func randBytes(length int) ([]byte, error) {
|
|
||||||
b := make([]byte, length)
|
|
||||||
_, err := rand.Read(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
return b, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// this creates a random ciphertext for demo purposes
|
|
||||||
// this is not intended to be reverseable or to be used in production
|
|
||||||
func encrypt(data []byte) ([]byte, error) {
|
|
||||||
b, err := randBytes(256 / 8)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
block, err := aes.NewCipher(b)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
gcm, err := cipher.NewGCM(block)
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
nonce := make([]byte, gcm.NonceSize())
|
|
||||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
ciphertext := gcm.Seal(nonce, nonce, data, nil)
|
|
||||||
return ciphertext, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// create a JWT (JSON Web Token) for demo purposes, this is not to be used in production
|
|
||||||
func createToken(id string, passwd string) (string, error) {
|
|
||||||
// create the token
|
|
||||||
token := jwt.New(jwt.SigningMethodHS256)
|
|
||||||
|
|
||||||
// set some claims
|
|
||||||
claims := make(jwt.MapClaims)
|
|
||||||
claims["username"] = id
|
|
||||||
claims["password"] = passwd
|
|
||||||
claims["exp"] = time.Now().Add(time.Hour * 24).Unix()
|
|
||||||
|
|
||||||
token.Claims = claims
|
|
||||||
|
|
||||||
//Sign and get the complete encoded token as string
|
|
||||||
return (token.SignedString([]byte("airshipui")))
|
|
||||||
}
|
|
@ -1,86 +0,0 @@
|
|||||||
<!doctype html>
|
|
||||||
<html>
|
|
||||||
|
|
||||||
<head>
|
|
||||||
<meta charset="utf-8">
|
|
||||||
<title>AirshipUI Test {{.Title}}</title>
|
|
||||||
<link rel="icon" href="data:;base64,=">
|
|
||||||
</head>
|
|
||||||
<script>
|
|
||||||
function testIt() {
|
|
||||||
document.getElementById("AuthBtn").disabled = true;
|
|
||||||
console.log(window.location.pathname);
|
|
||||||
let xhr = new XMLHttpRequest();
|
|
||||||
xhr.open("POST", window.location.pathname);
|
|
||||||
xhr.setRequestHeader("Content-Type", "application/json;charset=UTF-8");
|
|
||||||
xhr.onload = function () {
|
|
||||||
if (this.status === 201) {
|
|
||||||
document.cookie = "airshipUI=" + xhr.response + "expires=" + new Date().getUTCDate;
|
|
||||||
console.log(JSON.parse(xhr.response));
|
|
||||||
} else {
|
|
||||||
console.log({
|
|
||||||
status: this.status,
|
|
||||||
statusText: xhr.statusText
|
|
||||||
});
|
|
||||||
document.getElementById("OutputDiv").innerHTML = '<span style="color:red"><h2>☠ ID or Password is incorrect please try again ☠</h2></span>';
|
|
||||||
document.getElementById("AuthBtn").disabled = false;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
xhr.onerror = function () {
|
|
||||||
reject({
|
|
||||||
status: this.status,
|
|
||||||
statusText: xhr.statusText
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
let json = JSON.stringify({"id": document.getElementById("ID").value, "password": document.getElementById("Passwd").value});
|
|
||||||
console.log(json)
|
|
||||||
xhr.send(json);
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<body>
|
|
||||||
<h1>Airship UI Test {{.Title}}</h1>
|
|
||||||
<table>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<b>Id:</b>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="text" id="ID">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td>
|
|
||||||
<b>Password:</b>
|
|
||||||
</td>
|
|
||||||
<td>
|
|
||||||
<input type="password" id="Passwd">
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
<tr>
|
|
||||||
<td colspan="2">
|
|
||||||
<button id="AuthBtn" onclick="testIt()">Test It!</button>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
</table>
|
|
||||||
<div id="OutputDiv"></div>
|
|
||||||
<h2>⚠ Warning! ⚠</h2>
|
|
||||||
<p>This is a {{.Title}} test page is only intended as an example for how to use {{.Title}} with AirshipUI.</p>
|
|
||||||
<p>The System will return the following HTML status codes and responses</p>
|
|
||||||
<ul>
|
|
||||||
{{if eq .Title "Basic Auth"}}
|
|
||||||
<li>201: Created. The password attempt was successful and the backend has sent an xauth token header to AirshipUI.</li>
|
|
||||||
{{else if eq .Title "Cookie"}}
|
|
||||||
<li>201: Created. The password attempt was successful and the backend has set a cookie and sent the cookie contents to AirshipUI.</li>
|
|
||||||
{{else if eq .Title "OAuth"}}
|
|
||||||
<li>201: Created. The password attempt was successful and the backend has set a JWT (JSON Web Token) and sent the JWT contents to AirshipUI.</li>
|
|
||||||
{{end}}
|
|
||||||
<li>400: Bad request. There was an error sending the system the authentication request, most likely bad JSON.</li>
|
|
||||||
<li>401: Unauthorized. Bad id / password attempt.</li>
|
|
||||||
<li>403: Forbidden. The id / password combination was correct but the id is not allowed for the resource.</li>
|
|
||||||
<li>500: Internal Server Error. There was a processing error on the back end.</li>
|
|
||||||
</ul>
|
|
||||||
</body>
|
|
||||||
|
|
||||||
</html>
|
|
1
go.mod
1
go.mod
@ -3,7 +3,6 @@ module opendev.org/airship/airshipui
|
|||||||
go 1.13
|
go 1.13
|
||||||
|
|
||||||
require (
|
require (
|
||||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
|
||||||
github.com/google/uuid v1.1.1
|
github.com/google/uuid v1.1.1
|
||||||
github.com/gorilla/websocket v1.4.2
|
github.com/gorilla/websocket v1.4.2
|
||||||
github.com/pkg/errors v0.9.1
|
github.com/pkg/errors v0.9.1
|
||||||
|
46
tools/password.go
Executable file
46
tools/password.go
Executable file
@ -0,0 +1,46 @@
|
|||||||
|
/*
|
||||||
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
|
you may not use this file except in compliance with the License.
|
||||||
|
You may obtain a copy of the License at
|
||||||
|
|
||||||
|
https://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
|
||||||
|
Unless required by applicable law or agreed to in writing, software
|
||||||
|
distributed under the License is distributed on an "AS IS" BASIS,
|
||||||
|
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||||
|
See the License for the specific language governing permissions and
|
||||||
|
limitations under the License.
|
||||||
|
*/
|
||||||
|
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/sha512"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rootCmd represents the base command when called without any subcommands
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: "go run password.go <password>",
|
||||||
|
Short: "Create an sha512 password hash",
|
||||||
|
Long: "This creates an sha512 password hash used for user authentication in the etc/airshipui.json conf file",
|
||||||
|
Run: launch,
|
||||||
|
}
|
||||||
|
|
||||||
|
// take the password argument and turn it into a hash
|
||||||
|
func launch(cmd *cobra.Command, args []string) {
|
||||||
|
if len(args) == 1 {
|
||||||
|
// create and disply the sha512 hash for the password
|
||||||
|
hash := sha512.New()
|
||||||
|
hash.Write([]byte(args[0]))
|
||||||
|
fmt.Printf("%x\n", hash.Sum(nil))
|
||||||
|
} else {
|
||||||
|
fmt.Println("There should be 1 password argument")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
rootCmd.Execute()
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user