providing server side config for key size and asset type support

This commit is contained in:
Roland Osborne 2022-09-01 00:22:43 -07:00
parent fd372187bc
commit 58a2984dbc
22 changed files with 217 additions and 89 deletions

View File

@ -3271,8 +3271,6 @@ components:
type: object
required:
- domain
- openAccess
- accountLimit
- accountStorage
properties:
domain:
@ -3280,11 +3278,14 @@ components:
accountStorage:
type: integer
format: int64
openAccess:
enableImage:
type: boolean
accountLimit:
type: integer
format: int64
enableAudio:
type: boolean
enableVideo:
type: boolean
keyType:
type: string
AccountStatus:
type: object
@ -3627,6 +3628,12 @@ components:
updated:
type: integer
format: int64
enableImage:
type: boolean
enableAudio:
type: boolean
enableVideo:
type: video
contacts:
$ref: '#/components/schemas/ChannelContacts'
members:

View File

@ -80,5 +80,9 @@ func AddChannel(w http.ResponseWriter, r *http.Request) {
for _, card := range cards {
SetContactChannelNotification(account, card)
}
WriteResponse(w, getChannelModel(slot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(slot, true, true, image, audio, video))
}

View File

@ -89,5 +89,9 @@ func ClearChannelCard(w http.ResponseWriter, r *http.Request) {
for _, card := range cards {
SetContactChannelNotification(account, &card)
}
WriteResponse(w, getChannelModel(&channelSlot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(&channelSlot, true, true, image, audio, video))
}

View File

@ -83,5 +83,9 @@ func ClearChannelGroup(w http.ResponseWriter, r *http.Request) {
for _, card := range cards {
SetContactChannelNotification(account, &card)
}
WriteResponse(w, getChannelModel(&channelSlot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(&channelSlot, true, true, image, audio, video))
}

View File

@ -19,8 +19,8 @@ func GetAccountAvailable(w http.ResponseWriter, r *http.Request) {
func getAvailableAccounts() (available int64, err error) {
open := getBoolConfigValue(CNFOpenAccess, true)
limit := getNumConfigValue(CNFAccountLimit, 16)
open := getBoolConfigValue(CNFOpenAccess, false)
limit := getNumConfigValue(CNFAccountLimit, 0)
var count int64
if err = store.DB.Model(&store.Account{}).Count(&count).Error; err != nil {

View File

@ -49,15 +49,19 @@ func GetChannelDetail(w http.ResponseWriter, r *http.Request) {
return
}
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
// return model data
if guid != "" {
if isChannelShared(guid, slot.Channel) {
WriteResponse(w, getChannelDetailModel(&slot, false))
WriteResponse(w, getChannelDetailModel(&slot, false, image, audio, video))
} else {
ErrResponse(w, http.StatusNotFound, errors.New("channel not shared with requestor"))
return
}
} else {
WriteResponse(w, getChannelDetailModel(&slot, true))
WriteResponse(w, getChannelDetailModel(&slot, true, image, audio, video))
}
}

View File

@ -50,6 +50,10 @@ func GetChannels(w http.ResponseWriter, r *http.Request) {
}
}
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
response := []*Channel{}
tokenType := ParamTokenType(r)
if tokenType == APPTokenAgent {
@ -80,7 +84,7 @@ func GetChannels(w http.ResponseWriter, r *http.Request) {
if channelRevisionSet {
response = append(response, getChannelRevisionModel(&slot, true))
} else if slot.Channel != nil {
response = append(response, getChannelModel(&slot, true, true))
response = append(response, getChannelModel(&slot, true, true, image, audio, video))
}
}
}
@ -124,7 +128,7 @@ func GetChannels(w http.ResponseWriter, r *http.Request) {
if channelRevisionSet {
response = append(response, getChannelRevisionModel(&slot, shared))
} else if shared {
response = append(response, getChannelModel(&slot, true, false))
response = append(response, getChannelModel(&slot, true, false, image, audio, video))
}
}
}

View File

@ -16,9 +16,11 @@ func GetNodeConfig(w http.ResponseWriter, r *http.Request) {
// get node config fields
var config NodeConfig
config.Domain = getStrConfigValue(CNFDomain, "")
config.AccountLimit = getNumConfigValue(CNFAccountLimit, 16)
config.OpenAccess = getBoolConfigValue(CNFOpenAccess, true)
config.AccountStorage = getNumConfigValue(CNFStorage, 0)
config.EnableImage = getBoolConfigValue(CNFEnableImage, true)
config.EnableAudio = getBoolConfigValue(CNFEnableAudio, true)
config.EnableVideo = getBoolConfigValue(CNFEnableVideo, true)
config.KeyType = getStrConfigValue(CNFKeyType, APPRSA4096)
WriteResponse(w, config)
}

View File

@ -90,5 +90,9 @@ func SetChannelCard(w http.ResponseWriter, r *http.Request) {
for _, card := range cards {
SetContactChannelNotification(account, &card)
}
WriteResponse(w, getChannelModel(&channelSlot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(&channelSlot, true, true, image, audio, video))
}

View File

@ -83,5 +83,9 @@ func SetChannelGroup(w http.ResponseWriter, r *http.Request) {
for _, card := range cards {
SetContactChannelNotification(account, &card)
}
WriteResponse(w, getChannelModel(&channelSlot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(&channelSlot, true, true, image, audio, video))
}

View File

@ -82,5 +82,9 @@ func SetChannelSubject(w http.ResponseWriter, r *http.Request) {
for _, card := range cards {
SetContactChannelNotification(account, &card)
}
WriteResponse(w, getChannelModel(&slot, true, true))
video := getBoolConfigValue(CNFEnableVideo, true);
audio := getBoolConfigValue(CNFEnableAudio, true);
image := getBoolConfigValue(CNFEnableImage, true);
WriteResponse(w, getChannelModel(&slot, true, true, image, audio, video))
}

View File

@ -34,22 +34,6 @@ func SetNodeConfig(w http.ResponseWriter, r *http.Request) {
return res
}
// upsert account limit config
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"num_value"}),
}).Create(&store.Config{ConfigID: CNFAccountLimit, NumValue: config.AccountLimit}).Error; res != nil {
return res
}
// upsert account open access
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"bool_value"}),
}).Create(&store.Config{ConfigID: CNFAccountLimit, BoolValue: config.OpenAccess}).Error; res != nil {
return res
}
// upsert account storage config
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
@ -58,6 +42,38 @@ func SetNodeConfig(w http.ResponseWriter, r *http.Request) {
return res
}
// upsert enable image processing
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"bool_value"}),
}).Create(&store.Config{ConfigID: CNFEnableImage, BoolValue: config.EnableImage}).Error; res != nil {
return res
}
// upsert enable audio processing
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"bool_value"}),
}).Create(&store.Config{ConfigID: CNFEnableAudio, BoolValue: config.EnableAudio}).Error; res != nil {
return res
}
// upsert enable video processing
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"bool_value"}),
}).Create(&store.Config{ConfigID: CNFEnableVideo, BoolValue: config.EnableVideo}).Error; res != nil {
return res
}
// upsert key type
if res := tx.Clauses(clause.OnConflict{
Columns: []clause.Column{{Name: "config_id"}},
DoUpdates: clause.AssignmentColumns([]string{"str_value"}),
}).Create(&store.Config{ConfigID: CNFKeyType, StrValue: config.KeyType}).Error; res != nil {
return res
}
return nil
})
if err != nil {

View File

@ -30,6 +30,18 @@ const CNFAssetPath = "asset_path"
//CNFScriptPath specifies the path where transform scripts are found
const CNFScriptPath = "script_path"
//CNFEnableImage specifies whether node can process image assets
const CNFEnableImage = "enable_image"
//CNFEnableAudio specifies whether node can process audio assets
const CNFEnableAudio = "enable_audio"
//CNFEnableVideo specifies whether node can process video assets
const CNFEnableVideo = "enable_video"
//CNFKeyType specifies the type of key to use for identity
const CNFKeyType = "key_type"
func getStrConfigValue(configID string, empty string) string {
var config store.Config
err := store.DB.Where("config_id = ?", configID).First(&config).Error

View File

@ -8,20 +8,14 @@ import (
"errors"
)
var keySize int = APPKeySize
//SetKeySize sets the key size to use for new accounts
func SetKeySize(size int) {
keySize = size
}
//GenerateRsaKeyPair creates a public/private key for a new account
func GenerateRsaKeyPair() (*rsa.PrivateKey, *rsa.PublicKey, string, error) {
if keySize == 2048 {
privkey, _ := rsa.GenerateKey(rand.Reader, keySize)
keyType := getStrConfigValue(CNFKeyType, "RSA4096");
if keyType == "RSA2048" {
privkey, _ := rsa.GenerateKey(rand.Reader, 2048)
return privkey, &privkey.PublicKey, "RSA2048", nil
} else if keySize == 4096 {
privkey, _ := rsa.GenerateKey(rand.Reader, keySize)
} else if keyType == "RSA4096" {
privkey, _ := rsa.GenerateKey(rand.Reader, 4096)
return privkey, &privkey.PublicKey, "RSA2048", nil
} else {
return nil, nil, "", errors.New("invalid key setting")

View File

@ -166,7 +166,7 @@ func getChannelRevisionModel(slot *store.ChannelSlot, showData bool) *Channel {
}
}
func getChannelDetailModel(slot *store.ChannelSlot, showList bool) *ChannelDetail {
func getChannelDetailModel(slot *store.ChannelSlot, showList bool, image bool, audio bool, video bool) *ChannelDetail {
if slot.Channel == nil {
return nil
@ -195,6 +195,9 @@ func getChannelDetailModel(slot *store.ChannelSlot, showList bool) *ChannelDetai
Data: slot.Channel.Data,
Created: slot.Channel.Created,
Updated: slot.Channel.Updated,
EnableImage: image,
EnableAudio: audio,
EnableVideo: video,
Contacts: contacts,
Members: members,
}
@ -221,7 +224,7 @@ func getChannelSummaryModel(slot *store.ChannelSlot) *ChannelSummary {
}
}
func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Channel {
func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool, image bool, audio bool, video bool) *Channel {
if !showData || slot.Channel == nil {
return &Channel{
@ -236,7 +239,7 @@ func getChannelModel(slot *store.ChannelSlot, showData bool, showList bool) *Cha
Data: &ChannelData{
DetailRevision: slot.Channel.DetailRevision,
TopicRevision: slot.Channel.TopicRevision,
ChannelDetail: getChannelDetailModel(slot, showList),
ChannelDetail: getChannelDetailModel(slot, showList, image, audio, video),
ChannelSummary: getChannelSummaryModel(slot),
},
}

View File

@ -178,6 +178,12 @@ type ChannelDetail struct {
Updated int64 `json:"updated"`
EnableImage bool `json:"enableImage"`
EnableAudio bool `json:"enableAudio"`
EnableVideo bool `json:"enableVideo"`
Contacts *ChannelContacts `json:"contacts,omitempty"`
Members []string `json:"members"`
@ -321,9 +327,13 @@ type LoginAccess struct {
type NodeConfig struct {
Domain string `json:"domain"`
OpenAccess bool `json:"openAccess"`
EnableImage bool `json:"enableImage"`
AccountLimit int64 `json:"accountLimit"`
EnableAudio bool `json:"enableAudio"`
EnableVideo bool `json:"enableVideo"`
KeyType string `json:"keyType"`
AccountStorage int64 `json:"accountStorage"`
}

View File

@ -1,5 +1,5 @@
import { DashboardWrapper, SettingsButton, AddButton, SettingsLayout, CreateLayout } from './Dashboard.styled';
import { Tooltip, Button, Modal, Input, InputNumber, Space, List } from 'antd';
import { Tooltip, Checkbox, Select, Button, Modal, Input, InputNumber, Space, List } from 'antd';
import { SettingOutlined, CopyOutlined, UserAddOutlined, LogoutOutlined, ReloadOutlined } from '@ant-design/icons';
import { useDashboard } from './useDashboard.hook';
import { AccountItem } from './accountItem/AccountItem';
@ -63,15 +63,35 @@ export function Dashboard({ token, config, logout }) {
<Modal title="Settings" visible={state.showSettings} centered
okText="Save" onOk={() => actions.setSettings()} onCancel={() => actions.setShowSettings(false)}>
<SettingsLayout direction="vertical">
<div class="host">
<div class="field">
<div>Federated Host:&nbsp;</div>
<Input placeholder="domain:port/app" onChange={(e) => actions.setHost(e.target.value)}
value={state.host} />
value={state.domain} />
</div>
<div class="storage">
<div class="field">
<div>Storage Limit (GB) / Account:&nbsp;</div>
<InputNumber defaultValue={8} onChange={(e) => actions.setStorage(e)}
placeholder="0 for unrestricted" value={state.storage} />
<InputNumber defaultValue={0} onChange={(e) => actions.setStorage(e)}
placeholder="0 for unrestricted" value={state.accountStorage} />
</div>
<div class="field">
<div>Account Key Type:&nbsp;</div>
<Select labelInValue defaultValue={{ value: 'RSA4096', label: 'RSA 4096' }}
value={state.keyType} onChange={(o) => actions.setKeyType(o.value)}>
<Select.Option value="RSA2048">RSA 2048</Select.Option>
<Select.Option value="RSA4096">RSA 4096</Select.Option>
</Select>
</div>
<div class="field">
<Checkbox onChange={(e) => actions.setEnableImage(e.target.checked)}
defaultChecked={true} checked={state.enableImage}>Enable Image Queue</Checkbox>
</div>
<div class="field">
<Checkbox onChange={(e) => actions.setEnableAudio(e.target.checked)}
defaultChecked={true} checked={state.enableAudio}>Enable Audio Queue</Checkbox>
</div>
<div class="field">
<Checkbox onChange={(e) => actions.setEnableVideo(e.target.checked)}
defaultChecked={true} checked={state.enableVideo}>Enable Video Queue</Checkbox>
</div>
</SettingsLayout>
</Modal>

View File

@ -71,14 +71,7 @@ export const SettingsButton = styled(Button)`
export const SettingsLayout = styled(Space)`
width: 100%;
.host {
white-space: nowrap;
display: flex;
flex-direction: row;
align-items: center;
}
.storage {
.field {
white-space: nowrap;
display: flex;
flex-direction: row;

View File

@ -7,8 +7,12 @@ import { addAccountCreate } from 'api/addAccountCreate';
export function useDashboard(token, config) {
const [state, setState] = useState({
host: "",
storage: null,
domain: "",
accountStorage: null,
keyType: null,
enableImage: null,
enableAudio: null,
enableVideo: null,
showSettings: false,
busy: false,
loading: false,
@ -42,11 +46,23 @@ export function useDashboard(token, config) {
await removeAccount(token, accountId);
actions.getAccounts();
},
setHost: (value) => {
updateState({ host: value });
setHost: (domain) => {
updateState({ domain });
},
setStorage: (value) => {
updateState({ storage: value });
setStorage: (accountStorage) => {
updateState({ accountStorage });
},
setKeyType: (keyType) => {
updateState({ keyType });
},
setEnableImage: (enableImage) => {
updateState({ enableImage });
},
setEnableAudio: (enableAudio) => {
updateState({ enableAudio });
},
setEnableVideo: (enableVideo) => {
updateState({ enableVideo });
},
setShowSettings: (value) => {
updateState({ showSettings: value });
@ -55,8 +71,10 @@ export function useDashboard(token, config) {
if (!state.busy) {
updateState({ busy: true });
try {
const { domain, keyType, accountStorage, enableImage, enableAudio, enableVideo } = state;
await setNodeConfig(token,
{ ...state.config, domain: state.host, accountStorage: state.storage * 1073741824 });
{ domain, accountStorage: accountStorage * 1073741824,
keyType, enableImage, enableAudio, enableVideo });
updateState({ showSettings: false });
}
catch(err) {
@ -92,13 +110,11 @@ export function useDashboard(token, config) {
};
useEffect(() => {
let storage = config.accountStorage / 1073741824;
if (storage > 1) {
storage = Math.ceil(storage);
}
updateState({ host: config.domain, storage: storage });
const { accountStorage, domain, keyType, enableImage, enableAudio, enableVideo } = config;
updateState({ domain, accountStorage: Math.ceil(accountStorage / 1073741824), keyType,
enableImage, enableAudio, enableVideo });
actions.getAccounts();
}, []);
}, [config]);
return { state, actions };
}

View File

@ -18,6 +18,9 @@ export function useConversationContext() {
members: new Set(),
topics: new Map(),
revision: null,
enableImage: null,
enabelAudio: null,
enableVideo: null,
});
const EVENT_OPEN = 1;
@ -198,12 +201,16 @@ export function useConversationContext() {
let subject = getSubject(chan);
let contacts = getContacts(chan);
let members = getMembers(chan);
const { enableImage, enableAudio, enableVideo } = chan.data.channelDetail;
updateState({
init: true,
error: false,
subject,
contacts,
members,
enableImage,
enableAudio,
enableVideo,
topics: topics.current,
revision: channelView.current.revision,
});

View File

@ -104,15 +104,21 @@ export function AddTopic({ cardId, channelId }) {
value={state.messageText} autocapitalize="none" />
</div>
<div class="buttons">
{ state.enableImage && (
<div class="button space" onClick={() => attachImage.current.click()}>
<PictureOutlined />
</div>
)}
{ state.enableVideo && (
<div class="button space" onClick={() => attachVideo.current.click()}>
<VideoCameraOutlined />
</div>
)}
{ state.enableAudio && (
<div class="button space" onClick={() => attachAudio.current.click()}>
<SoundOutlined />
</div>
)}
<div class="bar space" />
<div class="button space">
<Dropdown overlay={picker} overlayStyle={{ minWidth: 0 }} trigger={['click']} placement="top">

View File

@ -1,10 +1,14 @@
import { useContext, useState } from 'react';
import { useContext, useState, useEffect } from 'react';
import { CardContext } from 'context/CardContext';
import { ChannelContext } from 'context/ChannelContext';
import { ConversationContext } from 'context/ConversationContext';
export function useAddTopic(cardId, channelId) {
const [state, setState] = useState({
enableImage: null,
enableAudio: null,
enableVideo: null,
assets: [],
messageText: null,
textColor: '#444444',
@ -16,6 +20,7 @@ export function useAddTopic(cardId, channelId) {
const card = useContext(CardContext);
const channel = useContext(ChannelContext);
const conversation = useContext(ConversationContext);
const updateState = (value) => {
setState((s) => ({ ...s, ...value }));
@ -43,6 +48,11 @@ export function useAddTopic(cardId, channelId) {
});
}
useEffect(() => {
const { enableImage, enableAudio, enableVideo } = conversation.state;
updateState({ enableImage, enableAudio, enableVideo });
}, [conversation]);
const actions = {
addImage: (image) => {
let url = URL.createObjectURL(image);