From bcc032413cd9df951dd5bca6686c73ee0c8b8342 Mon Sep 17 00:00:00 2001 From: ansible user/allowed to read system logs Date: Mon, 7 Aug 2023 13:52:15 -0700 Subject: [PATCH] mobile styles and info panel --- client/src/App.tsx | 56 ++++++++++++-- client/src/api/bridge.ts | 2 - client/src/components/groups/groupPanel.tsx | 2 +- client/src/components/groups/groupRow.tsx | 2 +- client/src/components/join/joinChannel.tsx | 2 +- client/src/components/join/joinForm.tsx | 8 +- client/src/components/join/joinGroup.tsx | 1 - client/src/components/join/joinStep.tsx | 17 ++++- client/src/components/modals/infoModal.tsx | 82 +++++++++++++++++++++ client/src/components/modePanel.tsx | 4 +- client/src/components/tabPanel.tsx | 3 +- client/src/sass/App.scss | 59 ++++++++++++--- client/src/sass/base.scss | 6 ++ client/src/sass/input.scss | 8 +- client/src/sass/tabs.scss | 50 ++++++++----- client/src/sass/typography.scss | 10 +++ client/src/sass/variables.scss | 6 +- client/src/types.d.ts | 10 +++ server/src/controllers/slack.controller.ts | 6 +- server/src/middleware/cookies.ts | 2 +- 20 files changed, 280 insertions(+), 56 deletions(-) create mode 100644 client/src/components/modals/infoModal.tsx diff --git a/client/src/App.tsx b/client/src/App.tsx index 856d5b6..8d01a9e 100644 --- a/client/src/App.tsx +++ b/client/src/App.tsx @@ -1,26 +1,68 @@ -import React from 'react'; +import React, {useState} from 'react'; import { createTheme, ThemeProvider } from '@mui/material/styles'; import { yellow } from "@mui/material/colors"; +import GitHubIcon from '@mui/icons-material/GitHub'; +import ModePanel from "./components/modePanel"; +import Button from "@mui/material/Button"; +import IconButton from "@mui/material/IconButton"; + +import InfoModal from "./components/modals/infoModal"; +import {Info} from "@mui/icons-material"; + +const REPO_URL = "https://github.com/sneakers-the-rat/chatbridge" const theme = createTheme({ palette:{ primary: yellow, - mode: "dark" + mode: "dark", } }) -import ModePanel from "./components/modePanel"; + + + + function App() { - return ( + + const [infoOpen, setInfoOpen] = useState(false); + + const handleInfo = () => { + setInfoOpen(!infoOpen) + } + + + return (
-
- ChatBridge -
+
+
+ ChatBridge +
+
+ + + +
+
diff --git a/client/src/api/bridge.ts b/client/src/api/bridge.ts index 6e4359d..0d60a8c 100644 --- a/client/src/api/bridge.ts +++ b/client/src/api/bridge.ts @@ -3,9 +3,7 @@ export const getBridgeByStateToken = (callback: CallableFunction) => { fetch('api/bridge') .then(res => res.json()) .then((res) => { - console.log('bridge result', res) if (res.status === 'success'){ - console.log('successful get bridge') callback(res.data) } }) diff --git a/client/src/components/groups/groupPanel.tsx b/client/src/components/groups/groupPanel.tsx index 09be21b..991fc98 100644 --- a/client/src/components/groups/groupPanel.tsx +++ b/client/src/components/groups/groupPanel.tsx @@ -22,8 +22,8 @@ export default function GroupPanel({ Group - Created At Invite Token + Created At Delete diff --git a/client/src/components/groups/groupRow.tsx b/client/src/components/groups/groupRow.tsx index 18888b3..460de77 100644 --- a/client/src/components/groups/groupRow.tsx +++ b/client/src/components/groups/groupRow.tsx @@ -32,8 +32,8 @@ export default function GroupRow( {name} - {created_at} {invite_token} + {created_at} { - if (bridge){ + if (bridge && !channels){ switch(platform){ case "Slack": getSlackChannels(setChannels) diff --git a/client/src/components/join/joinForm.tsx b/client/src/components/join/joinForm.tsx index fd31272..4751cd4 100644 --- a/client/src/components/join/joinForm.tsx +++ b/client/src/components/join/joinForm.tsx @@ -59,11 +59,11 @@ export const JoinForm = ({group}: JoinFormProps) => { return ( <>
- Joining group: {group.name} + {group.name}
@@ -78,7 +78,7 @@ export const JoinForm = ({group}: JoinFormProps) => { { } aria-controls="panel1bh-content" id="panel1bh-header" > - + { title } + {details !== '' ? { details } + : undefined} { completed ? @@ -50,9 +63,7 @@ export function JoinStep( } - { children } - ) diff --git a/client/src/components/modals/infoModal.tsx b/client/src/components/modals/infoModal.tsx new file mode 100644 index 0000000..8e208eb --- /dev/null +++ b/client/src/components/modals/infoModal.tsx @@ -0,0 +1,82 @@ +import * as React from "react"; +import Dialog from '@mui/material/Dialog'; +import DialogTitle from '@mui/material/DialogTitle'; +import DialogContent from '@mui/material/DialogContent'; +import DialogContentText from "@mui/material/DialogContentText"; + +export interface InfoModalProps { + open: boolean + setOpen: CallableFunction +} + +const InfoModal = ({ + open, + setOpen +}: InfoModalProps) => { + + const handleClose = () => { + setOpen(false) + } + + + return( + + + ChatBridge INFO + + + + ChatBridge is a tool for making groupchats that span multiple chat protocols and platforms. + + It is a web frontend for matterbridge and manages the API logins for platforms like Slack and Discord to lower the barriers to + bridging. +

+ This is ALPHA software and functionality is not guaranteed! The eventual goal is to make guppe groups for proprietary chat platforms, where anyone can create and join groups with just an invite token. + For now, creating groups is limited to an administrator token until we can be more sure about the security and performance demands of running many matterbridge processes. +
+

Security & Privacy Notes

+ + This tool is made by privacy advocates and activists. The developers have no interest in storing or monetizing your information, full stop. We will never abuse the app tokens created by logging into the ChatBridge App for anything except configuring the underlying matterbridge processes +

+ + To be as transparent as possible about the operation of the service: +
+

What is stored

+ +
    +
  • Slack/Discord: App login tokens that are generated for your workspace/server
  • +
  • Limited metadata about your bridged chat, specifically its name and unique ID (usually these are considered public anyway), and the short label you provide to the service
  • +
  • The names of channels you have bridged
  • +
+
+

What is NOT stored

+ +
    +
  • Message content and metadata of any kind, even in debugging logs
  • +
  • No other metadata about your bridged chat except for that listed above - channel lists in the interface are requested and discarded during the API call
  • +
  • The bot access tokens can NOT access DMs or private channels
  • +
+
+

Security of Data at Rest

+ + Matterbridge relies on .toml files that contain the app tokens in plain text. This sets a hard limit on how secure the data can be at rest - ie. encryption at rest is not possible. The tokens that are granted to ChatBridge CAN be used to exfiltrate chat history of public channels for slack and discord if they were to be lost in a data breach. We therfore do NOT recommend you use ChatBridge in a context where the contents of your public channels or the membership in your chatroom becoming public could pose a risk to your members. +

+ For the main development instance, the service is run as its own user, and the configuration files are stored such that only that user can read them. For the data to be breached, an attacker would need to compromise a root SSH key. +

+ For information accessed through the chatbridge interface, we authenticate using ephemeral signed cookies that expire 24 hours after they are issued. +

+ We of course can make no guarantee about security of data at any other instances that deploy ChatBridge. +

+ To revoke access to ChatBridge at any time, you can uninstall the app from your slack workspace or discord server - this makes the access keys obsolete and will require them to be reissued if you choose to rejoin ChatBridge. +
+
+ +
+ ) +} + +export default InfoModal \ No newline at end of file diff --git a/client/src/components/modePanel.tsx b/client/src/components/modePanel.tsx index 3347b89..9cf9eef 100644 --- a/client/src/components/modePanel.tsx +++ b/client/src/components/modePanel.tsx @@ -30,8 +30,8 @@ export default function ModePanel() { onChange={handleChange} selectionFollowsFocus> {/**/} - - + + {/**/} diff --git a/client/src/components/tabPanel.tsx b/client/src/components/tabPanel.tsx index e3682c3..b138afc 100644 --- a/client/src/components/tabPanel.tsx +++ b/client/src/components/tabPanel.tsx @@ -14,7 +14,8 @@ export default function TabPanel(props) { {...other} > {value === index && ( - + {children} )} diff --git a/client/src/sass/App.scss b/client/src/sass/App.scss index e7d8353..0b9081e 100644 --- a/client/src/sass/App.scss +++ b/client/src/sass/App.scss @@ -11,6 +11,8 @@ //} border: 2px solid $color-primary; height:100%; + box-sizing: border-box; + overflow-y: auto; } .App-Container { @@ -23,6 +25,11 @@ width:100%; height:100%; box-sizing: border-box; + + @media (max-width: $breakpoint-mobile) { + padding: 0; + box-sizing: border-box; + } } @@ -37,17 +44,49 @@ } } -.App-header { - text-align: center; - margin: { - top: 1em; - } +.App-header-bar{ display: flex; - flex-direction: column; - align-items: center; - justify-content: center; - font-size: calc(10px + 2vmin); - color: white; + flex-direction: row; + padding: 0.5em 0.5em; + gap: 0.5em; + font: { + family: "Courier", monospace; + } + + background: repeating-linear-gradient( + 45deg, + $color-primary, + $color-primary 20px, + $color-background 20px, + $color-background 40px + ); + + .header-padding { + flex-grow: 1; + } + .App-header-item { + font: { + family: "Courier", monospace; + } + } +} + + + +.App-header { + font: { + size: calc(10px + 2vmin); + weight: bold; + } + margin: auto; + color: $color-background; + background-color: $color-primary; + padding: 0.25em; + text-align: left; + //border-radius: 4px; + //box-shadow: 0px 2px 4px -1px rgba(0,0,0,0.2), + // 0px 4px 5px 0px rgba(0,0,0,0.14), + // 0px 1px 10px 0px rgba(0,0,0,0.12); } .App-link { diff --git a/client/src/sass/base.scss b/client/src/sass/base.scss index 3388e7e..4ea85c8 100644 --- a/client/src/sass/base.scss +++ b/client/src/sass/base.scss @@ -18,3 +18,9 @@ code { font-family: source-code-pro, Menlo, Monaco, Consolas, 'Courier New', monospace; } + +//@media (max-width: $breakpoint-mobile){ +// .MuiBox-root { +// padding: 6px !important; +// } +//} \ No newline at end of file diff --git a/client/src/sass/input.scss b/client/src/sass/input.scss index ff21604..d3465d0 100644 --- a/client/src/sass/input.scss +++ b/client/src/sass/input.scss @@ -4,6 +4,10 @@ align-items: flex-start; gap: 2em; + @media (max-width: $breakpoint-mobile) { + gap: 0.5em; + } + &>div { flex-basis: 50%; } @@ -39,7 +43,9 @@ gap: 2em; align-items: flex-start; - + @media (max-width: $breakpoint-mobile){ + gap: 0.5em; + } .Input { flex-basis: 70%; diff --git a/client/src/sass/tabs.scss b/client/src/sass/tabs.scss index b0f9052..8362871 100644 --- a/client/src/sass/tabs.scss +++ b/client/src/sass/tabs.scss @@ -1,7 +1,10 @@ .ModePanel { - max-width: 80%; + //max-width: 80%; margin: auto; + font: { + family: "Courier", monospace; + } .TabsList { min-width: 400px; @@ -19,22 +22,7 @@ justify-content: center; align-content: space-between; - .Tab { - font-family: 'IBM Plex Sans', sans-serif; - color: white; - cursor: pointer; - font-size: 1.5rem; - font-weight: bold; - background-color: transparent; - width: 100%; - line-height: 1.5; - padding: 8px 12px; - margin: 6px; - border-radius: 8px; - display: flex; - justify-content: center; - border: none; - } + .Tab.Mui-selected { background-color: $color-primary; @@ -42,4 +30,32 @@ } } + .Tab { + //font-size: 1000px; + font: { + family: 'Courier', monospace; + size: 1.2em; + } + color: white; + cursor: pointer; + //font-size: 1.5rem; + //font-weight: bold; + background-color: transparent; + width: 100%; + line-height: 1.5; + padding: 8px 12px; + margin: 6px; + border-radius: 8px; + display: flex; + justify-content: center; + border: none; + } +} + +.TabPanel { + padding: 24px; + + @media (max-width: $breakpoint-mobile){ + padding: 12px 12px; + } } \ No newline at end of file diff --git a/client/src/sass/typography.scss b/client/src/sass/typography.scss index d05b545..6c8f7b0 100644 --- a/client/src/sass/typography.scss +++ b/client/src/sass/typography.scss @@ -10,6 +10,16 @@ } } +p a { + background-color: $color-primary; + color: $color-background; + padding: 0.2em; + font: { + family: "Courier", monospace; + } + border-radius: 3px; +} + code { font: { family: 'Courier', 'Courier New', monospace; diff --git a/client/src/sass/variables.scss b/client/src/sass/variables.scss index aa5d3e8..2b22fd6 100644 --- a/client/src/sass/variables.scss +++ b/client/src/sass/variables.scss @@ -1,7 +1,9 @@ -$color-primary: #DED03A; +$color-primary: #ffeb3b; $color-primary-darker: scale-color($color-primary, $lightness: -10%); $color-primary-background: adjust-color($color-primary, $alpha: -0.9, $saturation: -50%); $color-primary-border: adjust-color($color-primary, $alpha: -0.9, $lightness: 50%, $saturation: -50%); $color-secondary: #A167A5; $color-background: #1C1B22; -$color-text: white; \ No newline at end of file +$color-text: white; + +$breakpoint-mobile: 600px; \ No newline at end of file diff --git a/client/src/types.d.ts b/client/src/types.d.ts index b623f68..f95d801 100644 --- a/client/src/types.d.ts +++ b/client/src/types.d.ts @@ -3,3 +3,13 @@ declare module "*.svg" { const content: any; export default content; } + +// declare module '@mui/material/styles' { +// interface Palette { +// paleYellow: Palette['primary']; +// } +// +// interface PaletteOptions { +// paleYellow?: PaletteOptions['primary']; +// } +// } \ No newline at end of file diff --git a/server/src/controllers/slack.controller.ts b/server/src/controllers/slack.controller.ts index 20abd8d..d01e33b 100644 --- a/server/src/controllers/slack.controller.ts +++ b/server/src/controllers/slack.controller.ts @@ -15,7 +15,7 @@ const scopes = ['bot', 'channels:write', 'channels:write.invites', 'chat:write:b const slackBridgeRepository = AppDataSource.getRepository(SlackBridge) const groupRepository = AppDataSource.getRepository(Group) -const SLACK_COOKIE_NAME = "slack-oauth-state"; +// const SLACK_COOKIE_NAME = "slack-oauth-state"; const slackConfig = config.get('slackConfig'); @@ -45,7 +45,7 @@ export const SlackInstallLinkHandler = async( state_token ); - res.cookie(SLACK_COOKIE_NAME, state_token, { maxAge: 60*5 }) + // res.cookie(SLACK_COOKIE_NAME, state_token, { maxAge: 60*5 }) res.status(200).json({ status: 'success', data: { @@ -78,7 +78,9 @@ export const SlackCallbackHandler = async( // check if we have an entity let bridge = await slackBridgeRepository.findOneBy({Token: installation.bot.token}) + logger.debug('found existing bridge %s', bridge) if (!bridge){ + logger.debug('creating new bridge') bridge = await slackBridgeRepository.create(bridge_data); await slackBridgeRepository.save(bridge); logger.debug('created bridge') diff --git a/server/src/middleware/cookies.ts b/server/src/middleware/cookies.ts index 35a73c1..4693639 100644 --- a/server/src/middleware/cookies.ts +++ b/server/src/middleware/cookies.ts @@ -19,7 +19,7 @@ export const cookieMiddleware = cookieSession({ name: 'session', keys: [cookieConfig.key1, cookieConfig.key2], signed: true, - + maxAge: 24*60*60*1000 // 24 hours }) export const requireAdmin = (req: Request, res: Response, next: NextFunction) => {