mirror of
https://github.com/balzack/databag.git
synced 2025-02-12 03:29:16 +00:00
switch to token auth for admin to support account image urls
This commit is contained in:
parent
23ff473286
commit
ab9935fac7
50
doc/api.oa3
50
doc/api.oa3
@ -84,8 +84,13 @@ paths:
|
||||
- admin
|
||||
description: Get node configuration. Access granted to admin username and password.
|
||||
operationId: get-node-config
|
||||
security:
|
||||
- basicAuth: []
|
||||
parameters:
|
||||
- name: token
|
||||
in: query
|
||||
description: token for admin access
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: success
|
||||
@ -102,8 +107,13 @@ paths:
|
||||
- admin
|
||||
description: Set node config. Access granted to admin username and password.
|
||||
operationId: set-node-config
|
||||
security:
|
||||
- basicAuth: []
|
||||
parameters:
|
||||
- name: token
|
||||
in: query
|
||||
description: token for admin access
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: success
|
||||
@ -118,8 +128,13 @@ paths:
|
||||
- admin
|
||||
description: Get list of accounts hosted on node. Access granted to admin username and password.
|
||||
operationId: get-node-accounts
|
||||
security:
|
||||
- basicAuth: []
|
||||
parameters:
|
||||
- name: token
|
||||
in: query
|
||||
description: token for admin access
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
@ -140,8 +155,13 @@ paths:
|
||||
- admin
|
||||
description: Gernerate a url for creating a new account. Access granted to admin username and password.
|
||||
operationId: add-node-account
|
||||
security:
|
||||
- basicAuth: []
|
||||
parameters:
|
||||
- name: token
|
||||
in: query
|
||||
description: token for admin access
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'201':
|
||||
description: success
|
||||
@ -160,8 +180,6 @@ paths:
|
||||
- admin
|
||||
description: Get profile image of node account.
|
||||
operationId: get-node-account-image
|
||||
security:
|
||||
- basicAuth: []
|
||||
parameters:
|
||||
- name: accountId
|
||||
in: path
|
||||
@ -169,6 +187,12 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: token
|
||||
in: query
|
||||
description: token for admin access
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
@ -199,6 +223,12 @@ paths:
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
- name: token
|
||||
in: query
|
||||
description: token for admin access
|
||||
required: true
|
||||
schema:
|
||||
type: string
|
||||
responses:
|
||||
'200':
|
||||
description: successful operation
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
|
||||
func AddNodeAccount(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if err := AdminLogin(r); err != nil {
|
||||
ErrResponse(w, http.StatusUnauthorized, err)
|
||||
if code, err := ParamAdminToken(r); err != nil {
|
||||
ErrResponse(w, code, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -22,8 +22,8 @@ func GetNodeAccountImage(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := AdminLogin(r); err != nil {
|
||||
ErrResponse(w, http.StatusUnauthorized, err)
|
||||
if code, err := ParamAdminToken(r); err != nil {
|
||||
ErrResponse(w, code, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
|
||||
func GetNodeAccounts(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
if err := AdminLogin(r); err != nil {
|
||||
ErrResponse(w, http.StatusUnauthorized, err)
|
||||
if code, err := ParamAdminToken(r); err != nil {
|
||||
ErrResponse(w, code, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -7,8 +7,8 @@ import (
|
||||
func GetNodeConfig(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// validate login
|
||||
if err := AdminLogin(r); err != nil {
|
||||
ErrResponse(w, http.StatusUnauthorized, err)
|
||||
if code, err := ParamAdminToken(r); err != nil {
|
||||
ErrResponse(w, code, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,8 @@ func RemoveNodeAccount(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if err := AdminLogin(r); err != nil {
|
||||
ErrResponse(w, http.StatusUnauthorized, err)
|
||||
if code, err := ParamAdminToken(r); err != nil {
|
||||
ErrResponse(w, code, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -20,8 +20,8 @@ func SetNodeAccount(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
if res = AdminLogin(r); res != nil {
|
||||
ErrResponse(w, http.StatusUnauthorized, res)
|
||||
if code, res := ParamAdminToken(r); res != nil {
|
||||
ErrResponse(w, code, res)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -10,8 +10,8 @@ import (
|
||||
func SetNodeConfig(w http.ResponseWriter, r *http.Request) {
|
||||
|
||||
// validate login
|
||||
if err := AdminLogin(r); err != nil {
|
||||
ErrResponse(w, http.StatusUnauthorized, err)
|
||||
if code, err := ParamAdminToken(r); err != nil {
|
||||
ErrResponse(w, code, err)
|
||||
return
|
||||
}
|
||||
|
||||
|
@ -20,18 +20,14 @@ func SetNodeStatus(w http.ResponseWriter, r *http.Request) {
|
||||
return
|
||||
}
|
||||
|
||||
username, password, res := BasicCredentials(r);
|
||||
if res != nil || username != "admin" {
|
||||
LogMsg("invalid credenitals");
|
||||
token := r.FormValue("token")
|
||||
if token == "" {
|
||||
w.WriteHeader(http.StatusBadRequest)
|
||||
return
|
||||
}
|
||||
|
||||
err = store.DB.Transaction(func(tx *gorm.DB) error {
|
||||
if res := tx.Create(&store.Config{ConfigId: CONFIG_USERNAME, StrValue: username}).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
if res := tx.Create(&store.Config{ConfigId: CONFIG_PASSWORD, BinValue: password}).Error; res != nil {
|
||||
if res := tx.Create(&store.Config{ConfigId: CONFIG_TOKEN, StrValue: token}).Error; res != nil {
|
||||
return res
|
||||
}
|
||||
if res := tx.Create(&store.Config{ConfigId: CONFIG_CONFIGURED, BoolValue: true}).Error; res != nil {
|
||||
|
@ -11,33 +11,6 @@ import (
|
||||
"databag/internal/store"
|
||||
)
|
||||
|
||||
func AdminLogin(r *http.Request) error {
|
||||
|
||||
// extract request auth
|
||||
username, password, ok := r.BasicAuth()
|
||||
if !ok || username == "" || password == "" {
|
||||
return errors.New("invalid credentials")
|
||||
}
|
||||
|
||||
// nothing to do if not configured
|
||||
if !getBoolConfigValue(CONFIG_CONFIGURED, false) {
|
||||
return errors.New("node not configured")
|
||||
}
|
||||
|
||||
// compare username
|
||||
if getStrConfigValue(CONFIG_USERNAME, "") != username {
|
||||
return errors.New("admin username error")
|
||||
}
|
||||
|
||||
// compare password
|
||||
p := getBinConfigValue(CONFIG_PASSWORD, nil);
|
||||
if bcrypt.CompareHashAndPassword(p, []byte(password)) != nil {
|
||||
return errors.New("admin password error")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func AccountLogin(r *http.Request) (*store.Account, error) {
|
||||
|
||||
// extract request auth
|
||||
@ -77,6 +50,28 @@ func BearerAccountToken(r *http.Request) (*store.AccountToken, error) {
|
||||
return &accountToken, nil
|
||||
}
|
||||
|
||||
func ParamAdminToken(r *http.Request) (int, error) {
|
||||
|
||||
// parse authentication token
|
||||
token := r.FormValue("token")
|
||||
if token == "" {
|
||||
return http.StatusUnauthorized, errors.New("token not set");
|
||||
}
|
||||
|
||||
// nothing to do if not configured
|
||||
if !getBoolConfigValue(CONFIG_CONFIGURED, false) {
|
||||
return http.StatusUnauthorized, errors.New("node not configured")
|
||||
}
|
||||
|
||||
// compare password
|
||||
value := getStrConfigValue(CONFIG_TOKEN, "");
|
||||
if (value != token) {
|
||||
return http.StatusUnauthorized, errors.New("invalid admin token")
|
||||
}
|
||||
|
||||
return http.StatusOK, nil;
|
||||
}
|
||||
|
||||
func ParamAgentToken(r *http.Request, detail bool) (*store.Account, int, error) {
|
||||
|
||||
// parse authentication token
|
||||
|
@ -9,8 +9,7 @@ import (
|
||||
const CONFIG_OPENACCESS = "open_access"
|
||||
const CONFIG_ACCOUNTLIMIT = "account_limit"
|
||||
const CONFIG_CONFIGURED = "configured"
|
||||
const CONFIG_USERNAME = "username"
|
||||
const CONFIG_PASSWORD = "password"
|
||||
const CONFIG_TOKEN = "token"
|
||||
const CONFIG_DOMAIN = "domain"
|
||||
const CONFIG_STORAGE = "storage"
|
||||
const CONFIG_ASSETPATH = "asset_path"
|
||||
|
@ -34,8 +34,7 @@ func TestMain(m *testing.M) {
|
||||
}
|
||||
|
||||
// claim server
|
||||
r, w, _ = NewRequest("PUT", "/admin/status", nil)
|
||||
SetCredentials(r, "admin:pass");
|
||||
r, w, _ = NewRequest("PUT", "/admin/status?token=pass", nil)
|
||||
SetNodeStatus(w, r)
|
||||
if ReadResponse(w, nil) != nil {
|
||||
panic("failed to claim server")
|
||||
@ -55,16 +54,14 @@ func TestMain(m *testing.M) {
|
||||
|
||||
// config server
|
||||
config := NodeConfig{Domain: "databag.coredb.org", AccountLimit: 1024, OpenAccess: true, AccountStorage: 4096}
|
||||
r, w, _ = NewRequest("PUT", "/admin/config", &config)
|
||||
SetBasicAuth(r, "admin:pass")
|
||||
r, w, _ = NewRequest("PUT", "/admin/config?token=pass", &config)
|
||||
SetNodeConfig(w, r)
|
||||
if ReadResponse(w, nil) != nil {
|
||||
panic("failed to set config")
|
||||
}
|
||||
|
||||
// check config
|
||||
r, w, _ = NewRequest("GET", "/admin/config", nil)
|
||||
SetBasicAuth(r, "admin:pass")
|
||||
r, w, _ = NewRequest("GET", "/admin/config?token=pass", nil)
|
||||
GetNodeConfig(w, r)
|
||||
var check NodeConfig
|
||||
if ReadResponse(w, &check) != nil {
|
||||
|
@ -620,10 +620,9 @@ func AddTestAccount(username string) (guid string, token string, err error) {
|
||||
var login = username + ":pass"
|
||||
|
||||
// get account token
|
||||
if r, w, err= NewRequest("POST", "/admin/accounts", nil); err != nil {
|
||||
if r, w, err= NewRequest("POST", "/admin/accounts?token=pass", nil); err != nil {
|
||||
return
|
||||
}
|
||||
SetBasicAuth(r, "admin:pass")
|
||||
AddNodeAccount(w, r)
|
||||
if err = ReadResponse(w, &access); err != nil {
|
||||
return
|
||||
|
4
net/web/src/Admin/Dashboard/AccountItem/AccountItem.jsx
Normal file
4
net/web/src/Admin/Dashboard/AccountItem/AccountItem.jsx
Normal file
@ -0,0 +1,4 @@
|
||||
export function AccountItem() {
|
||||
return <div>ACCOUNT</div>
|
||||
}
|
||||
|
@ -1,7 +1,8 @@
|
||||
import { DashboardWrapper, SettingsButton, AddButton, SettingsLayout } from './Dashboard.styled';
|
||||
import { Button, Modal, Input, InputNumber, Space } from 'antd';
|
||||
import { SettingOutlined, UserAddOutlined } from '@ant-design/icons';
|
||||
import { Button, Modal, Input, InputNumber, Space, List } from 'antd';
|
||||
import { SettingOutlined, UserAddOutlined, ReloadOutlined } from '@ant-design/icons';
|
||||
import { useDashboard } from './useDashboard.hook';
|
||||
import { AccountItem } from './AccountItem/AccountItem';
|
||||
|
||||
export function Dashboard({ password, config }) {
|
||||
|
||||
@ -13,6 +14,10 @@ export function Dashboard({ password, config }) {
|
||||
<div class="container">
|
||||
<div class="header">
|
||||
<div class="label">Accounts</div>
|
||||
<div class="settings">
|
||||
<SettingsButton type="text" size="small" icon={<ReloadOutlined />}
|
||||
onClick={() => actions.getAccounts()}></SettingsButton>
|
||||
</div>
|
||||
<div class="settings">
|
||||
<SettingsButton type="text" size="small" icon={<SettingOutlined />}
|
||||
onClick={() => actions.setShowSettings(true)}></SettingsButton>
|
||||
@ -21,10 +26,20 @@ export function Dashboard({ password, config }) {
|
||||
<AddButton type="text" size="large" icon={<UserAddOutlined />}></AddButton>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="body">
|
||||
<List
|
||||
locale={{ emptyText: '' }}
|
||||
itemLayout="horizontal"
|
||||
dataSource={state.accounts}
|
||||
loading={state.loading}
|
||||
renderItem={item => (<AccountItem item={item} />)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<Modal title="Settings" visible={state.showSettings} centered
|
||||
okText="Save" onOk={() => actions.onSaveSettings()} onCancel={() => actions.setShowSettings(false)}>
|
||||
okText="Save" onOk={() => actions.setSettings()} onCancel={() => actions.setShowSettings(false)}>
|
||||
<SettingsLayout direction="vertical">
|
||||
<div class="host">
|
||||
<div>Federated Host: </div>
|
||||
|
@ -17,33 +17,39 @@ export const DashboardWrapper = styled.div`
|
||||
border-radius: 4px;
|
||||
max-width: 500px;
|
||||
width: 50%;
|
||||
}
|
||||
max-height: 80%;
|
||||
|
||||
.header {
|
||||
color: #444444;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 20px;
|
||||
border-bottom: 1px solid #444444;
|
||||
}
|
||||
.header {
|
||||
color: #444444;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
font-size: 20px;
|
||||
border-bottom: 1px solid #444444;
|
||||
}
|
||||
|
||||
.label {
|
||||
padding-right: 8px;
|
||||
padding-left: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.body {
|
||||
min-height: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.settings {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
.label {
|
||||
padding-right: 8px;
|
||||
padding-left: 4px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.add {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-grow: 1;
|
||||
.settings {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.add {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
flex-grow: 1;
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
import { useState, useEffect } from 'react';
|
||||
import { setNodeConfig } from 'api/setNodeConfig';
|
||||
import { getNodeAccounts } from 'api/getNodeAccounts';
|
||||
|
||||
export function useDashboard(password, config) {
|
||||
|
||||
@ -8,20 +9,14 @@ export function useDashboard(password, config) {
|
||||
storage: null,
|
||||
showSettings: false,
|
||||
busy: false,
|
||||
loading: false,
|
||||
accounts: [],
|
||||
});
|
||||
|
||||
const updateState = (value) => {
|
||||
setState((s) => ({ ...s, ...value }));
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
let storage = config.accountStorage / 1073741824;
|
||||
if (storage > 1) {
|
||||
storage = Math.ceil(storage);
|
||||
}
|
||||
updateState({ host: config.domain, storage: storage });
|
||||
}, []);
|
||||
|
||||
const actions = {
|
||||
setHost: (value) => {
|
||||
updateState({ host: value });
|
||||
@ -32,7 +27,7 @@ export function useDashboard(password, config) {
|
||||
setShowSettings: (value) => {
|
||||
updateState({ showSettings: value });
|
||||
},
|
||||
onSaveSettings: async () => {
|
||||
setSettings: async () => {
|
||||
if (!state.busy) {
|
||||
updateState({ busy: true });
|
||||
try {
|
||||
@ -47,8 +42,31 @@ export function useDashboard(password, config) {
|
||||
updateState({ busy: false });
|
||||
}
|
||||
},
|
||||
getAccounts: async () => {
|
||||
if (!state.loading) {
|
||||
updateState({ loading: true });
|
||||
try {
|
||||
let accounts = await getNodeAccounts(password);
|
||||
updateState({ accounts });
|
||||
}
|
||||
catch(err) {
|
||||
console.log(err);
|
||||
window.alert(err);
|
||||
}
|
||||
updateState({ loading: false });
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
let storage = config.accountStorage / 1073741824;
|
||||
if (storage > 1) {
|
||||
storage = Math.ceil(storage);
|
||||
}
|
||||
updateState({ host: config.domain, storage: storage });
|
||||
actions.getAccounts();
|
||||
}, []);
|
||||
|
||||
return { state, actions };
|
||||
}
|
||||
|
||||
|
@ -42,7 +42,8 @@ export function useAdmin() {
|
||||
setAccess: async () => {
|
||||
try {
|
||||
await setNodeStatus(state.token);
|
||||
updateState({ access: state.token, unclaimed: false });
|
||||
let config = await getNodeConfig(state.token);
|
||||
updateState({ access: state.token, unclaimed: false, config });
|
||||
}
|
||||
catch(err) {
|
||||
console.log(err);
|
||||
|
8
net/web/src/api/getNodeAccounts.js
Normal file
8
net/web/src/api/getNodeAccounts.js
Normal file
@ -0,0 +1,8 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
|
||||
export async function getNodeAccounts(token) {
|
||||
let accounts = await fetchWithTimeout(`/admin/accounts?token=${token}`, { method: 'GET' });
|
||||
checkResponse(accounts);
|
||||
return await accounts.json();
|
||||
}
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
var base64 = require('base-64');
|
||||
|
||||
export async function getNodeConfig(password) {
|
||||
let headers = new Headers()
|
||||
headers.append('Authorization', 'Basic ' + base64.encode("admin:" + password));
|
||||
let config = await fetchWithTimeout(`/admin/config`, { method: 'GET', headers });
|
||||
export async function getNodeConfig(token) {
|
||||
let config = await fetchWithTimeout(`/admin/config?token=${token}`, { method: 'GET' });
|
||||
checkResponse(config);
|
||||
return await config.json();
|
||||
}
|
||||
|
@ -1,11 +1,8 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
var base64 = require('base-64');
|
||||
|
||||
export async function setNodeConfig(password, config) {
|
||||
export async function setNodeConfig(token, config) {
|
||||
let body = JSON.stringify(config);
|
||||
let headers = new Headers()
|
||||
headers.append('Authorization', 'Basic ' + base64.encode("admin:" + password));
|
||||
let settings = await fetchWithTimeout(`/admin/config`, { method: 'PUT', headers, body });
|
||||
let settings = await fetchWithTimeout(`/admin/config?token=${token}`, { method: 'PUT', body });
|
||||
checkResponse(settings);
|
||||
}
|
||||
|
||||
|
@ -1,10 +1,7 @@
|
||||
import { checkResponse, fetchWithTimeout } from './fetchUtil';
|
||||
var base64 = require('base-64');
|
||||
|
||||
export async function setNodeStatus(password) {
|
||||
let headers = new Headers()
|
||||
headers.append('Credentials', 'Basic ' + base64.encode("admin:" + password));
|
||||
let status = await fetchWithTimeout(`/admin/status`, { method: 'PUT', headers });
|
||||
export async function setNodeStatus(token) {
|
||||
let status = await fetchWithTimeout(`/admin/status?token=${token}`, { method: 'PUT' });
|
||||
checkResponse(status);
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user