瀏覽代碼

baby screaming... working on redirects after posts

bmallred 10 年之前
父節點
當前提交
71588e4f29
共有 4 個文件被更改,包括 385 次插入55 次删除
  1. 二進制
      084e0343a0486ff05530df6c705c8bb4
  2. 0 4
      book.html
  3. 55 51
      main.go
  4. 330 0
      resources.go

二進制
084e0343a0486ff05530df6c705c8bb4


+ 0 - 4
book.html

255
255
256
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
256
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
257
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
257
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
258
    <script>
259
        $(function () {
260
        })
261
    </script>
262
</body>
258
</body>
263
</html>
259
</html>

+ 55 - 51
main.go

14
	"regexp"
14
	"regexp"
15
	"strconv"
15
	"strconv"
16
	"strings"
16
	"strings"
17
	"text/template"
17
)
18
)
18
19
19
type Site struct {
20
type Site struct {
25
	NumberOfUpperCase         int    `json:numberOfUpperCase`
26
	NumberOfUpperCase         int    `json:numberOfUpperCase`
26
	NumberOfDigits            int    `json:numberOfDigits`
27
	NumberOfDigits            int    `json:numberOfDigits`
27
	Revision                  int    `json:revision`
28
	Revision                  int    `json:revision`
29
	Password                  string `json:",omitempty"`
30
}
31
32
type Page struct {
33
	Profile    string
34
	Passphrase string
35
	Sites      []Site
28
}
36
}
29
37
30
func main() {
38
func main() {
56
		}
64
		}
57
65
58
		book := getBookname(profile)
66
		book := getBookname(profile)
59
		sites, err := Read(book, passphrase)
67
		sites, err := read(book, passphrase)
60
		if err != nil {
68
		if err != nil {
61
			http.Error(w, err.Error(), http.StatusInternalServerError)
69
			http.Error(w, err.Error(), http.StatusInternalServerError)
62
			return
70
			return
63
		}
71
		}
64
		sites = append(sites, site)
72
		sites = append(sites, site)
65
		err = Save(book, passphrase, sites)
73
		err = save(book, passphrase, sites)
66
		if err != nil {
74
		if err != nil {
67
			http.Error(w, err.Error(), http.StatusInternalServerError)
75
			http.Error(w, err.Error(), http.StatusInternalServerError)
68
			return
76
			return
69
		}
77
		}
78
		http.Redirect(w, r, "/", 200)
70
	})
79
	})
71
	http.HandleFunc("/api/update", func(w http.ResponseWriter, r *http.Request) {
80
	http.HandleFunc("/api/update", func(w http.ResponseWriter, r *http.Request) {
72
		profile := r.FormValue("profile")
81
		profile := r.FormValue("profile")
92
		} else if cmd == "update" {
101
		} else if cmd == "update" {
93
			if newPassphrase != confirmPassphrase {
102
			if newPassphrase != confirmPassphrase {
94
			}
103
			}
95
			sites, err := Read(book, passphrase)
104
			sites, err := read(book, passphrase)
96
			if err != nil {
105
			if err != nil {
97
				http.Error(w, err.Error(), http.StatusInternalServerError)
106
				http.Error(w, err.Error(), http.StatusInternalServerError)
98
				return
107
				return
99
			}
108
			}
100
			err = Save(book, newPassphrase, sites)
109
			err = save(book, newPassphrase, sites)
101
			if err != nil {
110
			if err != nil {
102
				http.Error(w, err.Error(), http.StatusInternalServerError)
111
				http.Error(w, err.Error(), http.StatusInternalServerError)
103
				return
112
				return
104
			}
113
			}
105
		}
114
		}
115
		http.Redirect(w, r, "/", 200)
106
	})
116
	})
107
	http.HandleFunc("/api/refresh", func(w http.ResponseWriter, r *http.Request) {
117
	http.HandleFunc("/api/refresh", func(w http.ResponseWriter, r *http.Request) {
108
		profile := r.FormValue("profile")
118
		profile := r.FormValue("profile")
116
126
117
		// Update the revision number and generate a new password
127
		// Update the revision number and generate a new password
118
		book := getBookname(profile)
128
		book := getBookname(profile)
119
		sites, err := Read(book, passphrase)
129
		sites, err := read(book, passphrase)
120
		if err != nil {
130
		if err != nil {
121
			http.Error(w, err.Error(), http.StatusInternalServerError)
131
			http.Error(w, err.Error(), http.StatusInternalServerError)
122
			return
132
			return
127
				break
137
				break
128
			}
138
			}
129
		}
139
		}
130
		err = Save(book, passphrase, sites)
140
		err = save(book, passphrase, sites)
131
		if err != nil {
141
		if err != nil {
132
			http.Error(w, err.Error(), http.StatusInternalServerError)
142
			http.Error(w, err.Error(), http.StatusInternalServerError)
133
			return
143
			return
134
		}
144
		}
145
		http.Redirect(w, r, "/", 200)
135
	})
146
	})
136
	http.HandleFunc("/api/remove", func(w http.ResponseWriter, r *http.Request) {
147
	http.HandleFunc("/api/remove", func(w http.ResponseWriter, r *http.Request) {
137
		profile := r.FormValue("profile")
148
		profile := r.FormValue("profile")
145
156
146
		// Remove the site from our book and save it
157
		// Remove the site from our book and save it
147
		book := getBookname(profile)
158
		book := getBookname(profile)
148
		sites, err := Read(book, passphrase)
159
		sites, err := read(book, passphrase)
149
		if err != nil {
160
		if err != nil {
150
			http.Error(w, err.Error(), http.StatusInternalServerError)
161
			http.Error(w, err.Error(), http.StatusInternalServerError)
151
			return
162
			return
156
				break
167
				break
157
			}
168
			}
158
		}
169
		}
159
		err = Save(book, passphrase, sites)
170
		err = save(book, passphrase, sites)
160
		if err != nil {
171
		if err != nil {
161
			http.Error(w, err.Error(), http.StatusInternalServerError)
172
			http.Error(w, err.Error(), http.StatusInternalServerError)
162
			return
173
			return
163
		}
174
		}
175
		http.Redirect(w, r, "/", 200)
164
	})
176
	})
165
177
166
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
178
	http.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {
167
		profile := r.FormValue("profile")
179
		profile := r.FormValue("profile")
168
		passphrase := r.FormValue("p")
180
		passphrase := r.FormValue("p")
169
		//path := strings.TrimPrefix(r.URL.Path, "/")
170
181
171
		if profile == "" || passphrase == "" {
182
		if profile == "" || passphrase == "" {
172
			// Index
183
			fmt.Fprintf(w, templateIndex)
173
			page, err := ioutil.ReadFile("index.html")
184
		} else {
185
			book := getBookname(profile)
186
			sites, err := read(book, passphrase)
174
			if err != nil {
187
			if err != nil {
175
				http.NotFound(w, r)
188
				http.Error(w, err.Error(), http.StatusInternalServerError)
176
				return
189
				return
177
			}
190
			}
191
			for _, s := range sites {
192
				p, err := generatePassphrase(profile, passphrase, s)
193
				if err != nil {
194
				}
195
				s.Password = fmt.Sprintf("%s", string(p))
196
			}
178
197
179
			fmt.Fprintf(w, string(page))
198
			page := Page{
180
		} else {
199
				Profile:    profile,
181
			// A passphrase has been entered
200
				Passphrase: passphrase,
182
			page, err := ioutil.ReadFile("book.html")
201
				Sites:      sites,
202
			}
203
204
			t := template.Must(template.New("book").Parse(templateBook))
205
			err = t.Execute(w, page)
183
			if err != nil {
206
			if err != nil {
184
				http.NotFound(w, r)
207
				http.Error(w, err.Error(), http.StatusInternalServerError)
185
				return
208
				return
186
			}
209
			}
187
188
			fmt.Fprintf(w, string(page))
189
		}
210
		}
190
	})
211
	})
191
212
192
	log.Fatal(http.ListenAndServe("localhost:8080", nil))
213
	log.Fatal(http.ListenAndServe("localhost:8080", nil))
193
194
	//execDir, err := filepath.Abs(filepath.Dir(os.Args[0]))
195
	//if err != nil {
196
	//	log.Fatal(err)
197
	//}
198
199
	//file := filepath.Join(execDir, "enigma.safe")
200
	//sites, err := Read(file)
201
	//if err != nil {
202
	//	log.Fatal(err)
203
	//}
204
	//log.Println(sites)
205
206
	//err = Save(file, sites)
207
	//if err != nil {
208
	//	log.Fatal(err)
209
	//}
210
}
214
}
211
215
212
func Save(file, passphrase string, sites []Site) error {
216
func save(file, passphrase string, sites []Site) error {
213
	// If the file doesn't exist then create it
217
	// If the file doesn't exist then create it
214
	if _, err := os.Stat(file); os.IsNotExist(err) {
218
	if _, err := os.Stat(file); os.IsNotExist(err) {
215
		_, err = os.Create(file)
219
		_, err = os.Create(file)
225
	}
229
	}
226
230
227
	// Compress the contents
231
	// Compress the contents
228
	buffer := bytes.NewBuffer(b)
232
	var buffer bytes.Buffer
229
	gzipWriter := gzip.NewWriter(buffer)
233
	gzip := gzip.NewWriter(&buffer)
230
	if err != nil {
234
	if err != nil {
231
		return err
235
		return err
232
	}
236
	}
233
	defer gzipWriter.Close()
237
	gzip.Write(b)
238
	gzip.Close()
239
234
	// Write to the file
240
	// Write to the file
235
	fi, err := os.OpenFile(file, os.O_WRONLY, 0666)
241
	fi, err := os.OpenFile(file, os.O_WRONLY, 0666)
236
	if err != nil {
242
	if err != nil {
237
		return err
243
		return err
238
	}
244
	}
239
	defer fi.Close()
240
	_, err = fi.Write(buffer.Bytes())
245
	_, err = fi.Write(buffer.Bytes())
241
	if err != nil {
246
	if err != nil {
242
		return err
247
		return err
243
	}
248
	}
249
	fi.Close()
244
250
245
	return nil
251
	return nil
246
}
252
}
247
253
248
// Read the password book
254
// Read the password book
249
func Read(file, passphrase string) ([]Site, error) {
255
func read(file, passphrase string) ([]Site, error) {
250
	// If the file doesn't exist yet no worries
256
	// If the file doesn't exist yet no worries
251
	if _, err := os.Stat(file); os.IsNotExist(err) {
257
	if _, err := os.Stat(file); os.IsNotExist(err) {
252
		return []Site{}, nil
258
		return []Site{}, nil
253
	}
259
	}
254
260
255
	// Bring in the compressed data
261
	// Bring in the compressed data
256
	log.Println("Reading compressed file")
262
	fi, err := os.Open(file)
257
	compressed, err := ioutil.ReadFile(file)
258
	if err != nil {
263
	if err != nil {
259
		return nil, err
264
		return nil, err
260
	}
265
	}
261
266
262
	// Decompress the file contents
267
	// Decompress the file contents
263
	log.Println("Decompressing")
268
	gzip, err := gzip.NewReader(fi)
264
	buffer := bytes.NewBuffer(compressed)
265
	gzipReader, err := gzip.NewReader(buffer)
266
	if err != nil {
269
	if err != nil {
267
		return nil, err
270
		return nil, err
268
	}
271
	}
269
	defer gzipReader.Close()
272
	decompressed, err := ioutil.ReadAll(gzip)
273
	gzip.Close()
270
274
271
	// Unmarshal the JSON information
275
	// Unmarshal the JSON information
272
	log.Println("Unmarshal")
273
	var sites []Site
276
	var sites []Site
274
	err = json.Unmarshal(buffer.Bytes(), &sites)
277
	err = json.Unmarshal(decompressed, &sites)
275
	if err != nil {
278
	if err != nil {
276
		return nil, err
279
		return nil, err
277
	}
280
	}
281
	fi.Close()
278
282
279
	return sites, nil
283
	return sites, nil
280
}
284
}
283
func getBookname(profile string) string {
287
func getBookname(profile string) string {
284
	hash := md5.New()
288
	hash := md5.New()
285
	hash.Write([]byte(profile))
289
	hash.Write([]byte(profile))
286
	return fmt.Sprintf("%s", string(hash.Sum(nil)))
290
	return fmt.Sprintf("%x", string(hash.Sum(nil)))
287
}
291
}
288
292
289
// Encrypt the password book
293
// Encrypt the password book

+ 330 - 0
resources.go

1
package main
2
3
const (
4
	templateIndex string = `<!doctype html>
5
<html lang="en">
6
<head>
7
    <meta charset="utf-8">
8
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
9
    <meta name="viewport" content="width=device-width, initial-scale=1">
10
11
    <title>Enigma Login</title>
12
13
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
14
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css" />
15
    <style type="text/css">
16
        body {
17
            padding-top: 40px;
18
            padding-bottom: 40px;
19
            background-color: #eee;
20
        }
21
22
        .form-signin {
23
            max-width: 330px;
24
            padding: 15px;
25
            margin: 0 auto;
26
        }
27
        .form-signin .form-signin-heading,
28
        .form-signin .checkbox {
29
            margin-bottom: 10px;
30
        }
31
        .form-signin .checkbox {
32
            font-weight: normal;
33
        }
34
        .form-signin .form-control {
35
            position: relative;
36
            height: auto;
37
            -webkit-box-sizing: border-box;
38
            -moz-box-sizing: border-box;
39
            box-sizing: border-box;
40
            padding: 10px;
41
            font-size: 16px;
42
        }
43
        .form-signin .form-control:focus {
44
            z-index: 2;
45
        }
46
        .form-signin input[type="email"] {
47
            margin-bottom: -1px;
48
            border-bottom-right-radius: 0;
49
            border-bottom-left-radius: 0;
50
        }
51
        .form-signin input[type="password"] {
52
            margin-bottom: 10px;
53
            border-top-left-radius: 0;
54
            border-top-right-radius: 0;
55
        }
56
    </style>
57
58
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
59
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
60
    <!--[if lt IE 9]>
61
        <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
62
        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
63
    <![endif]-->
64
</head>
65
<body>
66
    <div class="container">
67
        <form action="/" method="post" class="form-signin">
68
            <h2 class="form-signin-heading">Please sign in</h2>
69
            <label for="profile" class="sr-only">Email address</label>
70
            <input type="text" id="profile" name="profile" class="form-control" placeholder="Username" required autofocus>
71
            <label for="p" class="sr-only">Password</label>
72
            <input type="password" id="p" name="p" class="form-control" placeholder="Password" required>
73
            <div class="checkbox">
74
                <label>
75
                    <input type="checkbox" value="remember-me"> Remember me
76
                </label>
77
            </div>
78
            <button class="btn btn-lg btn-primary btn-block" type="submit">Sign in</button>
79
        </form>
80
    </div>
81
82
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
83
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
84
</body>
85
</html>`
86
87
	templateBook string = `<!doctype html>
88
<html lang="en">
89
<head>
90
    <meta charset="utf-8">
91
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
92
    <meta name="viewport" content="width=device-width, initial-scale=1">
93
94
    <title>Enigma</title>
95
96
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap.min.css" />
97
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/css/bootstrap-theme.min.css" />
98
    <style type="text/css">
99
        body {
100
            padding-top: 40px;
101
            padding-bottom: 40px;
102
            background-color: #fff;
103
        }
104
        h2 {
105
            margin-bottom: 1em;
106
        }
107
        td {
108
            text-align: left;
109
            vertical-align: middle !important;
110
        }
111
        .tab-content > .tab-pane {
112
            padding: 1em;
113
        }
114
    </style>
115
116
    <!-- HTML5 shim and Respond.js for IE8 support of HTML5 elements and media queries -->
117
    <!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
118
    <!--[if lt IE 9]>
119
        <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script>
120
        <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script>
121
    <![endif]-->
122
</head>
123
<body>
124
    <div class="container">
125
        <h1>Enigma</h1>
126
        <h2><small>Your personal password safe and generator</small></h2>
127
128
		{{ $profile := .Profile }}
129
		{{ $passphrase := .Passphrase }}
130
131
        <div role="tabpanel">
132
            <ul class="nav nav-tabs" role="tablist">
133
                <li role="presentation" class="active"><a href="#passwords" aria-controls="passwords" role="tab" data-toggle="tab">Passwords</a></li>
134
                <li role="presentation"><a href="#add" aria-controls="Add" role="tab" data-toggle="tab">Add</a></li>
135
                <li role="presentation"><a href="#settings" aria-controls="settings" role="tab" data-toggle="tab">Settings</a></li>
136
            </ul>
137
            <div class="tab-content">
138
                <div role="tabpanel" class="tab-pane active" id="passwords">
139
                    <table class="table table-striped table-hover">
140
                        <thead>
141
                            <tr>
142
                                <th>Site</th>
143
                                <th>Password</th>
144
                                <th>&nbsp;</th>
145
                            </tr>
146
                        </thead>
147
                        <tbody>
148
							{{ range .Sites }}
149
                            <tr>
150
                                <td>{{ .Host }}</td>
151
                                <td>
152
                                    <button class="btn btn-default btn-xs"><span class="glyphicon glyphicon-share" aria-hidden="true"></span></button>
153
                                    <form class="form form-horizontal" style="display: inline-block;" action="/api/refresh" method="post">
154
                                        <input name="profile" type="hidden" value="{{ $profile }}" />
155
                                        <input name="p" type="hidden" value="{{ $passphrase }}" />
156
                                        <input name="host" type="hidden" value="{{ .Host }}" />
157
                                        <button class="btn btn-default btn-xs"><span class="glyphicon glyphicon-refresh" aria-hidden="true"></span></button>
158
                                    </form>
159
                                    <span>{{ .Password }}</span>
160
                                </td>
161
                                <td class="text-right">
162
                                    <form class="form form-horizontal" action="/api/remove" method="post">
163
                                        <input name="profile" type="hidden" value="{{ $profile }}" />
164
                                        <input name="p" type="hidden" value="{{ $passphrase }}" />
165
                                        <input name="host" type="hidden" value="{{ .Host }}" />
166
                                        <button class="btn btn-default"><span class="glyphicon glyphicon-remove" aria-hidden="true"></span></button>
167
                                    </form>
168
                                </td>
169
                            </tr>
170
							{{ end }}
171
                        </tbody>
172
                    </table>
173
                </div>
174
                <div role="tabpanel" class="tab-pane" id="add">
175
                    <form class="form form-horizontal" action="/api/generate" method="post">
176
						<input name="profile" type="hidden" value="{{ $profile }}" />
177
						<input name="p" type="hidden" value="{{ $passphrase }}" />
178
                        <div class="form-group">
179
                            <label for="host" class="col-xs-3 control-label">Site</label>
180
                            <div class="col-xs-9">
181
                                <input id="host" name="host" type="text" class="form-control" placeholder="gmail.com" />
182
                            </div>
183
                        </div>
184
                        <div class="form-group">
185
                            <label for="minimumLength" class="col-xs-3 control-label">Minimum Length</label>
186
                            <div class="col-xs-3">
187
                                <select id="minimumLength" name="minimumLength" class="form-control">
188
                                    <option value="-1">No Limit</option>
189
                                    <option>1</option>
190
                                    <option>2</option>
191
                                    <option>3</option>
192
                                    <option>4</option>
193
                                    <option>5</option>
194
                                    <option>6</option>
195
                                    <option>7</option>
196
                                    <option>8</option>
197
                                    <option>9</option>
198
                                    <option>10</option>
199
                                </select>
200
                            </div>
201
                        </div>
202
                        <div class="form-group">
203
                            <label for="maximumLength" class="col-xs-3 control-label">Maximum Length</label>
204
                            <div class="col-xs-3">
205
                                <select id="maximumLength" name="maximumLength" class="form-control">
206
                                    <option value="-1">No Limit</option>
207
                                    <option>4</option>
208
                                    <option>5</option>
209
                                    <option>6</option>
210
                                    <option>7</option>
211
                                    <option>8</option>
212
                                    <option>9</option>
213
                                    <option>10</option>
214
                                    <option>11</option>
215
                                    <option>12</option>
216
                                    <option>13</option>
217
                                    <option>14</option>
218
                                    <option>15</option>
219
                                    <option>16</option>
220
                                    <option>17</option>
221
                                    <option>18</option>
222
                                    <option>19</option>
223
                                    <option>20</option>
224
                                </select>
225
                            </div>
226
                        </div>
227
                        <div class="form-group">
228
                            <label for="minimumDigits" class="col-xs-3 control-label">Minimum Digits</label>
229
                            <div class="col-xs-3">
230
                                <select id="minimumDigits" name="minimumLength" class="form-control">
231
                                    <option>0</option>
232
                                    <option>1</option>
233
                                    <option>2</option>
234
                                    <option>3</option>
235
                                    <option>4</option>
236
                                    <option>5</option>
237
                                    <option>6</option>
238
                                    <option>7</option>
239
                                    <option>8</option>
240
                                    <option>9</option>
241
                                    <option>10</option>
242
                                </select>
243
                            </div>
244
                        </div>
245
                        <div class="form-group">
246
                            <label for="minimumUppercase" class="col-xs-3 control-label">Minimum Uppercase</label>
247
                            <div class="col-xs-3">
248
                                <select id="minimumUppercase" name="minimumUppercase" class="form-control">
249
                                    <option>0</option>
250
                                    <option>1</option>
251
                                    <option>2</option>
252
                                    <option>3</option>
253
                                    <option>4</option>
254
                                    <option>5</option>
255
                                    <option>6</option>
256
                                    <option>7</option>
257
                                    <option>8</option>
258
                                    <option>9</option>
259
                                    <option>10</option>
260
                                </select>
261
                            </div>
262
                        </div>
263
                        <div class="form-group">
264
                            <label for="minimumSpecialCharacters" class="col-xs-3 control-label">Minimum Special Characters</label>
265
                            <div class="col-xs-3">
266
                                <select id="minimumSpecialCharacters" name="minimumSpecialCharacters" class="form-control">
267
                                    <option>0</option>
268
                                    <option>1</option>
269
                                    <option>2</option>
270
                                    <option>3</option>
271
                                    <option>4</option>
272
                                    <option>5</option>
273
                                    <option>6</option>
274
                                    <option>7</option>
275
                                    <option>8</option>
276
                                    <option>9</option>
277
                                    <option>10</option>
278
                                </select>
279
                            </div>
280
                        </div>
281
                        <div class="form-group">
282
                            <label for="specialCharacters" class="col-xs-3 control-label">Special Characters</label>
283
                            <div class="col-xs-9">
284
                                <input id="specialCharacters" name="specialCharacters" type="text" class="form-control" value=" !@#$%^&*()_+-=<>,." />
285
                            </div>
286
                        </div>
287
                        <div class="form-group">
288
                            <div class="col-xs-offset-3 col-xs-10">
289
                                <button type="submit" class="btn btn-default">Generate Password</button>
290
                            </div>
291
                        </div>
292
                    </form>
293
                </div>
294
                <div role="tabpanel" class="tab-pane" id="settings">
295
                    <div class="row">
296
                        <form class="form form-horizontal" action="/api/update" method="post">
297
							<input name="profile" type="hidden" value="{{ $profile }}" />
298
							<input name="p" type="hidden" value="{{ $passphrase }}" />
299
                            <div class="form-group">
300
                                <label for="newPassphrase" class="col-xs-3 control-label">New passphrase</label>
301
                                <div class="col-xs-9">
302
                                    <input id="newPassphrase" type="password" class="form-control" placeholder="" />
303
                                </div>
304
                            </div>
305
                            <div class="form-group">
306
                                <label for="confirmPassphrase" class="col-xs-3 control-label">Confirm passphrase</label>
307
                                <div class="col-xs-9">
308
                                    <input id="confirmPassphrase" type="password" class="form-control" placeholder="" />
309
                                </div>
310
                            </div>
311
                            <div class="form-group">
312
                                <div class="col-xs-offset-3 col-xs-6">
313
                                    <button name="cmd" value="update" type="submit" class="btn btn-default">Update Passphrase</button>
314
                                </div>
315
                                <div class="col-xs-3 text-right">
316
                                    <button name="cmd" value="delete" type="submit" class="btn btn-danger">Delete Profile</button>
317
                                </div>
318
                            </div>
319
                        </form>
320
                    </div>
321
                </div>
322
            </div>
323
        </div>
324
    </div>
325
326
    <script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.3/jquery.min.js"></script>
327
    <script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.1/js/bootstrap.min.js"></script>
328
</body>
329
</html>`
330
)