support editing sealed messages in webapp

This commit is contained in:
Roland Osborne 2022-12-16 12:02:12 -08:00
parent b43a26b240
commit 8a6411ff21
8 changed files with 121 additions and 58 deletions

View File

@ -1,9 +1,9 @@
import { checkResponse, fetchWithTimeout } from './fetchUtil';
export async function setChannelTopicSubject(token, channelId, topicId, data) {
export async function setChannelTopicSubject(token, channelId, topicId, datatype, data) {
let subject = { data: JSON.stringify(data, (key, value) => {
if (value !== null) return value
}), datatype: 'superbasictopic' };
}), datatype };
let channel = await fetchWithTimeout(`/content/channels/${channelId}/topics/${topicId}/subject?agent=${token}&confirm=true`,
{ method: 'PUT', body: JSON.stringify(subject) });

View File

@ -278,22 +278,24 @@ export function useCardContext() {
const channel = card.channels.get(channelId);
const { subjectEncrypted, subjectIv, seals } = JSON.parse(channel.data.channelDetail.data);
seals.forEach(seal => {
if (seal.publicKey === sealKey.public) {
let crypto = new JSEncrypt();
crypto.setPrivateKey(sealKey.private);
const unsealedKey = crypto.decrypt(seal.sealedKey);
const iv = CryptoJS.enc.Hex.parse(subjectIv);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const enc = CryptoJS.enc.Base64.parse(subjectEncrypted);
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
channel.data.unsealedChannel = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
card.channels.set(channel.id, { ...channel });
cards.current.set(cardId, { ...card });
updateState({ cards: cards.current });
}
});
if (seals?.length) {
seals.forEach(seal => {
if (seal.publicKey === sealKey.public) {
let crypto = new JSEncrypt();
crypto.setPrivateKey(sealKey.private);
const unsealedKey = crypto.decrypt(seal.sealedKey);
const iv = CryptoJS.enc.Hex.parse(subjectIv);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const enc = CryptoJS.enc.Base64.parse(subjectEncrypted);
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
channel.data.unsealedChannel = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
card.channels.set(channel.id, { ...channel });
cards.current.set(cardId, { ...card });
updateState({ cards: cards.current });
}
});
}
},
removeChannel: async (cardId, channelId) => {
let { cardProfile, cardDetail } = cards.current.get(cardId).data;
@ -318,7 +320,27 @@ export function useCardContext() {
let { cardProfile, cardDetail } = cards.current.get(cardId).data;
let token = cardProfile.guid + '.' + cardDetail.token;
let node = cardProfile.node;
await setContactChannelTopicSubject(node, token, channelId, topicId, data);
await setContactChannelTopicSubject(node, token, channelId, topicId, 'superbasictopic', data);
try {
resync.current.push(cardId);
await setCards(null);
}
catch (err) {
console.log(err);
}
},
setSealedChannelTopicSubject: async (cardId, channelId, topicId, data, sealKey) => {
console.log("SETTING:", data, sealKey);
let { cardProfile, cardDetail } = cards.current.get(cardId).data;
let token = cardProfile.guid + '.' + cardDetail.token;
let node = cardProfile.node;
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const key = CryptoJS.enc.Hex.parse(sealKey);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify(data), key, { iv: iv });
const messageEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
const messageIv = iv.toString();
await setContactChannelTopicSubject(node, token, channelId, topicId, 'sealedtopic', { messageEncrypted, messageIv });
try {
resync.current.push(cardId);
await setCards(null);

View File

@ -134,21 +134,23 @@ export function useChannelContext() {
const channel = channels.current.get(channelId);
const { subjectEncrypted, subjectIv, seals } = JSON.parse(channel.data.channelDetail.data);
seals.forEach(seal => {
if (seal.publicKey === sealKey.public) {
let crypto = new JSEncrypt();
crypto.setPrivateKey(sealKey.private);
const unsealedKey = crypto.decrypt(seal.sealedKey);
const iv = CryptoJS.enc.Hex.parse(subjectIv);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const enc = CryptoJS.enc.Base64.parse(subjectEncrypted);
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
channel.data.unsealedChannel = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
channels.current.set(channel.id, { ...channel });
updateState({ channels: channels.current });
}
});
if (seals?.length) {
seals.forEach(seal => {
if (seal.publicKey === sealKey.public) {
let crypto = new JSEncrypt();
crypto.setPrivateKey(sealKey.private);
const unsealedKey = crypto.decrypt(seal.sealedKey);
const iv = CryptoJS.enc.Hex.parse(subjectIv);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const enc = CryptoJS.enc.Base64.parse(subjectEncrypted);
let cipher = CryptoJS.lib.CipherParams.create({ ciphertext: enc, iv: iv });
const dec = CryptoJS.AES.decrypt(cipher, key, { iv: iv });
channel.data.unsealedChannel = JSON.parse(dec.toString(CryptoJS.enc.Utf8));
channels.current.set(channel.id, { ...channel });
updateState({ channels: channels.current });
}
});
}
},
setChannelSubject: async (channelId, subject) => {
return await setChannelSubject(access.current, channelId, 'superbasic', { subject });
@ -157,19 +159,21 @@ export function useChannelContext() {
const channel = channels.current.get(channelId);
let { seals, subjectEncrypted, subjectIv } = JSON.parse(channel.data.channelDetail.data);
seals.forEach(seal => {
if (seal.publicKey === sealKey.public) {
let crypto = new JSEncrypt();
crypto.setPrivateKey(sealKey.private);
const unsealedKey = crypto.decrypt(seal.sealedKey);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
if (seals?.length) {
seals.forEach(seal => {
if (seal.publicKey === sealKey.public) {
let crypto = new JSEncrypt();
crypto.setPrivateKey(sealKey.private);
const unsealedKey = crypto.decrypt(seal.sealedKey);
const key = CryptoJS.enc.Hex.parse(unsealedKey);
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ subject }), key, { iv: iv });
subjectEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
subjectIv = iv.toString();
}
});
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ subject }), key, { iv: iv });
subjectEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
subjectIv = iv.toString();
}
});
}
const data = { subjectEncrypted, subjectIv, seals };
return await setChannelSubject(access.current, channelId, 'sealed', data);
},
@ -192,7 +196,21 @@ export function useChannelContext() {
}
},
setChannelTopicSubject: async (channelId, topicId, data) => {
await setChannelTopicSubject(access.current, channelId, topicId, data);
await setChannelTopicSubject(access.current, channelId, topicId, 'superbasictopic', data);
try {
await setChannels(null);
}
catch (err) {
console.log(err);
}
},
setSealedChannelTopicSubject: async (channelId, topicId, data, sealKey) => {
const iv = CryptoJS.lib.WordArray.random(128 / 8);
const key = CryptoJS.enc.Hex.parse(sealKey);
const encrypted = CryptoJS.AES.encrypt(JSON.stringify({ message: data }), key, { iv: iv });
const messageEncrypted = encrypted.ciphertext.toString(CryptoJS.enc.Base64)
const messageIv = iv.toString();
await setChannelTopicSubject(access.current, channelId, topicId, 'sealedtopic', { messageEncrypted, messageIv });
try {
await setChannels(null);
}

View File

@ -60,7 +60,7 @@ export function useConversationContext() {
const getSeals = (conversation) => {
try {
if (conversation.data.channelDetail.dataType === 'sealed') {
if (conversation?.data.channelDetail.dataType === 'sealed') {
return JSON.parse(conversation.data.channelDetail.data).seals;
}
}
@ -226,10 +226,11 @@ export function useConversationContext() {
if (curView === view.current) {
let chan = getChannel();
let contacts = getContacts(chan);
let subject = getSubject(chan);
let members = getMembers(chan);
const seals = getSeals(chan);
let seals = getSeals(chan);
const enableImage = chan?.data?.channelDetail?.enableImage;
const enableAudio = chan?.data?.channelDetail?.enableAudio;
const enableVideo = chan?.data?.channelDetail?.enableVideo;
@ -391,6 +392,15 @@ export function useConversationContext() {
return await channel.actions.setChannelTopicSubject(channelId, topicId, data);
}
},
setSealedTopicSubject: async (topicId, data, sealKey) => {
const { cardId, channelId } = channelView.current;
if (cardId) {
return await card.actions.setSealedChannelTopicSubject(cardId, channelId, topicId, data, sealKey);
}
else {
return await channel.actions.setSealedChannelTopicSubject(channelId, topicId, data, sealKey);
}
},
resync: () => {
updateState({ error: false });
events.current.push({ type: EVENT_RESYNC });

View File

@ -200,7 +200,7 @@ export function useChannels() {
}
const setMessage = (chan) => {
let message = "";
let message;
if (chan.data.channelSummary?.lastTopic?.dataType === 'superbasictopic') {
try {
message = JSON.parse(chan.data.channelSummary.lastTopic.data).text;
@ -210,7 +210,9 @@ export function useChannels() {
}
}
chan.message = message;
if (typeof message === 'string') {
chan.message = message;
}
}
useEffect(() => {

View File

@ -14,7 +14,7 @@ export function Conversation({ closeConversation, openDetails, cardId, channelId
const thread = useRef(null);
const topicRenderer = (topic) => {
return (<TopicItem host={cardId == null} topic={topic} sealKey={state.sealKey} />)
return (<TopicItem host={cardId == null} topic={topic} sealed={state.sealed} sealKey={state.sealKey} />)
}
// an unfortunate cludge for the mobile browser

View File

@ -8,9 +8,9 @@ import { Space, Skeleton, Button, Modal, Input } from 'antd';
import { ExclamationCircleOutlined, DeleteOutlined, EditOutlined, FireOutlined, PictureOutlined } from '@ant-design/icons';
import { Carousel } from 'carousel/Carousel';
export function TopicItem({ host, topic, sealKey }) {
export function TopicItem({ host, topic, sealed, sealKey }) {
const { state, actions } = useTopicItem(topic, sealKey);
const { state, actions } = useTopicItem(topic, sealed, sealKey);
let name = state.name ? state.name : state.handle;
let nameClass = state.name ? 'set' : 'unset';

View File

@ -3,7 +3,7 @@ import { ConversationContext } from 'context/ConversationContext';
import { ProfileContext } from 'context/ProfileContext';
import { CardContext } from 'context/CardContext';
export function useTopicItem(topic, sealKey) {
export function useTopicItem(topic, sealed, sealKey) {
const [state, setState] = useState({
init: false,
@ -60,7 +60,9 @@ export function useTopicItem(topic, sealKey) {
if (dataType === 'superbasictopic') {
try {
message = JSON.parse(data);
text = message.text;
if (typeof message.text === 'string') {
text = message.text;
}
if (message.textColor != null) {
textColor = message.textColor;
}
@ -84,7 +86,9 @@ export function useTopicItem(topic, sealKey) {
}
else if (dataType === 'sealedtopic') {
if (topic.data.unsealedMessage) {
text = topic.data.unsealedMessage.message.text;
console.log("UNSEALED MESSAGE", topic.data.unsealedMessage);
text = topic.data.unsealedMessage.message?.text;
sealed = false;
}
else {
@ -141,8 +145,15 @@ export function useTopicItem(topic, sealKey) {
if (!state.busy) {
updateState({ busy: true });
try {
await conversation.actions.setTopicSubject(topic.id,
if (sealed) {
console.log("SET SEALED");
await conversation.actions.setSealedTopicSubject(topic.id, {...state.message, text: editMessage.current }, sealKey);
}
else {
console.log("SET UNSEALED");
await conversation.actions.setTopicSubject(topic.id,
{ ...state.message, text: editMessage.current, assets: state.assets });
}
updateState({ editing: false });
}
catch (err) {