From c7dda3db6c3c4cd1c1023daa62a730b9d1e2a309 Mon Sep 17 00:00:00 2001 From: Yann Stepienik Date: Wed, 4 Oct 2023 18:26:47 +0100 Subject: [PATCH] [release] v0.10.0-unstable17 --- LICENCE | 62 ++++++++++++----- changelog.md | 2 + cla.md | 2 +- .../DrawerContent/Navigation/NavGroup.jsx | 2 +- .../DrawerContent/Navigation/NavItem.jsx | 7 ++ client/src/menu-items/pages.jsx | 5 +- client/src/pages/authentication/Logoff.jsx | 2 +- client/src/pages/config/users/configman.jsx | 67 +++++++++++++++++-- client/src/pages/constellation/addDevice.jsx | 23 +++++-- client/src/pages/constellation/dns.jsx | 14 ++-- client/src/pages/constellation/index.jsx | 28 +++++++- client/src/pages/constellation/vpn.jsx | 13 ++-- client/src/pages/home/index.jsx | 27 +++++--- client/src/pages/market/listing.jsx | 21 +++--- client/src/utils/hooks.js | 26 +++++++ package-lock.json | 32 ++++++++- package.json | 3 +- src/constellation/DNS.go | 2 +- src/constellation/api_devices_block.go | 2 + src/constellation/api_devices_config.go | 41 ++++++++++++ src/constellation/api_devices_create.go | 8 ++- src/constellation/api_nebula_connect.go | 2 +- src/constellation/nebula.go | 10 +-- src/market/index.go | 3 +- src/user/token.go | 25 +++++++ src/utils/types.go | 3 +- src/utils/utils.go | 2 +- 27 files changed, 353 insertions(+), 81 deletions(-) create mode 100644 client/src/utils/hooks.js create mode 100644 src/constellation/api_devices_config.go diff --git a/LICENCE b/LICENCE index 954fd0ae2cc4ab0774160c3534bb44a6292d6da2..1cadf6af2222d4625390416ebce70f6e7b2989a4 100644 --- a/LICENCE +++ b/LICENCE @@ -1,31 +1,57 @@ -“Commons Clause” License Condition v1.0 +Software: Cosmos-Server -The Software is provided to you by the Licensor under the -License, as defined below, subject to the following condition. +License: Apache 2.0 with Commons Clause and Anti Tampering Clause -Without limiting other conditions in the License, the grant -of rights under the License will not include, and the License -does not grant to you, the right to Sell the Software. +Licensor: Yann Stepienik -For purposes of the foregoing, “Sell” means practicing any or -all of the rights granted to you under the License to provide -to third parties, for a fee or other consideration (including -without limitation fees for hosting or consulting/ support -services related to the Software), a product or service whose -value derives, entirely or substantially, from the functionality -of the Software. Any license notice or attribution required by -the License must also include this Commons Clause License -Condition notice. +--------------------------------------------------------------------- -Software: Cosmos-Server + “Commons Clause” License Condition v1.0 -License: Apache 2.0 with Commons Clause + The Software is provided to you by the Licensor under the + License, as defined below, subject to the following condition. -Licensor: Yann Stepienik + Without limiting other conditions in the License, the grant + of rights under the License will not include, and the License + does not grant to you, the right to Sell the Software. + For purposes of the foregoing, “Sell” means practicing any or + all of the rights granted to you under the License to provide + to third parties, for a fee or other consideration (including + without limitation fees for hosting or consulting/ support + services related to the Software), a product or service whose + value derives, entirely or substantially, from the functionality + of the Software. Any license notice or attribution required by + the License must also include this Commons Clause License + Condition notice. --------------------------------------------------------------------- + "Anti Tampering Clause” License Condition v1.0 + + Notwithstanding any provision of the Apache License 2.0, if the User + (or any party receiving or distributing derivative works, services, + or anything of value from the User related to the Software), directly + or indirectly, seeks to tamper with, alter, circumvent, or avoid + compliance with any subscription, paywall, feature restriction, or any + other licensing mechanism built into the Software or its usage, the + License granted under the Apache License 2.0 shall automatically and + immediately terminate, and access to the Software shall be withdrawn + with immediate effect. Upon such termination, any and all rights + established under the Apache License 2.0 shall be null and void. + + Tampering includes but is not limited to: (a) removing, disabling, + or circumventing any license key or other copy protection mechanism, + (b) redistributing parts or all of a feature that was intended + to be a paid feature, without keeping the restrictions, limitations, + or other licensing mechanisms with it(c) disabling, circumventing, or + avoiding any feature of the Software that is intended to enforce usage or + copy restrictions, or (d) providing or distributing any information + or code that enables disabling, circumvention, or avoidance of any + feature of the Software that is intended to enforce usage or copy + restrictions. + +--------------------------------------------------------------------- Apache License Version 2.0, January 2004 diff --git a/changelog.md b/changelog.md index 1f51c44f89bbb55a5c0a4be233e902a2919ab788..ad8ed2b91ace4083068642935b992a82b34f2cdb 100644 --- a/changelog.md +++ b/changelog.md @@ -7,6 +7,8 @@ - Added Constellation - DNS Challenge is now used for all certificates when enabled - Rework headers for better compatibility + - Improve experience for non-admin users + - Fix bug with redirect on logout ## Version 0.9.20 - 0.9.21 - Add option to disable CORS hardening (with empty value) diff --git a/cla.md b/cla.md index 147f9d2b59b991106ba990bd9cd07eb1d7b8632f..22a970dde82fae72de41e4a889b073ff1bf3bef1 100644 --- a/cla.md +++ b/cla.md @@ -2,7 +2,7 @@ Cosmos Software Grant and Contributor License Agreement (“Agreement”) This agreement is based on the Apache Software Foundation Contributor License Agreement. (v r190612) -Thank you for your interest in software projects stewarded by Raintank, Inc. dba Cosmos (“Cosmos”). In order to clarify the intellectual property license granted with Contributions from any person or entity, Cosmos must have a Contributor License Agreement (CLA) on file that has been agreed to by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of Cosmos and its users; it does not change your rights to use your own Contributions for any other purpose. This Agreement allows an individual to contribute to Cosmos on that individual’s own behalf, or an entity (the “Corporation”) to submit Contributions to Cosmos, to authorize Contributions submitted by its designated employees to Cosmos, and to grant copyright and patent licenses thereto. +Thank you for your interest in dba Cosmos (“Cosmos”). In order to clarify the intellectual property license granted with Contributions from any person or entity, Cosmos must have a Contributor License Agreement (CLA) on file that has been agreed to by each Contributor, indicating agreement to the license terms below. This license is for your protection as a Contributor as well as the protection of Cosmos and its users; it does not change your rights to use your own Contributions for any other purpose. This Agreement allows an individual to contribute to Cosmos on that individual’s own behalf, or an entity (the “Corporation”) to submit Contributions to Cosmos, to authorize Contributions submitted by its designated employees to Cosmos, and to grant copyright and patent licenses thereto. You accept and agree to the following terms and conditions for Your present and future Contributions submitted to Cosmos. Except for the license granted herein to Cosmos and recipients of software distributed by Cosmos, You reserve all right, title, and interest in and to Your Contributions. diff --git a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.jsx b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.jsx index 9c001af9acf470202dee774bebf2467e81d4227c..77a13650d44203f74ac1bb50fa6b875da49f365e 100644 --- a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.jsx +++ b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavGroup.jsx @@ -47,7 +47,7 @@ const NavGroup = ({ item }) => { } sx={{ mb: drawerOpen ? 1.5 : 0, py: 0, zIndex: 0 }} > - {navCollapse} + {navCollapse} ); }; diff --git a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx index 77a3df03f45d5a3a4f4144e3d88b5ebaf9aff4fa..deb2ddf7d555f915680967c5ccaed5163f04a425 100644 --- a/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx +++ b/client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx @@ -9,6 +9,7 @@ import { Avatar, Chip, ListItemButton, ListItemIcon, ListItemText, Typography } // project import import { activeItem } from '../../../../../store/reducers/menu'; +import { useClientInfos } from '../../../../../utils/hooks'; // ==============================|| NAVIGATION - LIST ITEM ||============================== // @@ -17,6 +18,12 @@ const NavItem = ({ item, level }) => { const dispatch = useDispatch(); const menu = useSelector((state) => state.menu); const { drawerOpen, openItem } = menu; + const {role} = useClientInfos(); + const isAdmin = role === 1; + + if (item.adminOnly && !isAdmin) { + return null; + } let itemTarget = '_self'; if (item.target) { diff --git a/client/src/menu-items/pages.jsx b/client/src/menu-items/pages.jsx index a2e406866960d7bb30857506bcbfd8c10424270b..171c841f8c712214bb1ff7c7dd35e04a6b5d443f 100644 --- a/client/src/menu-items/pages.jsx +++ b/client/src/menu-items/pages.jsx @@ -20,7 +20,8 @@ const pages = { title: 'ServApps', type: 'item', url: '/cosmos-ui/servapps', - icon: AppstoreOutlined + icon: AppstoreOutlined, + adminOnly: true }, { id: 'url', @@ -43,6 +44,7 @@ const pages = { type: 'item', url: '/cosmos-ui/config-users', icon: icons.ProfileOutlined, + adminOnly: true }, { id: 'openid', @@ -50,6 +52,7 @@ const pages = { type: 'item', url: '/cosmos-ui/openid-manage', icon: PicLeftOutlined, + adminOnly: true }, { id: 'config', diff --git a/client/src/pages/authentication/Logoff.jsx b/client/src/pages/authentication/Logoff.jsx index 635efc0d8f4cdc428be0a6ee1c8feb09f6da85e9..3e7924fadbd1052fc89dfee2d77ce15ba8439e43 100644 --- a/client/src/pages/authentication/Logoff.jsx +++ b/client/src/pages/authentication/Logoff.jsx @@ -9,7 +9,7 @@ import AuthWrapper from './AuthWrapper'; import { useEffect } from 'react'; import * as API from '../../api'; -import { redirectTo } from '../../utils/indexs'; +import { redirectTo, redirectToLocal } from '../../utils/indexs'; // ================================|| REGISTER ||================================ // diff --git a/client/src/pages/config/users/configman.jsx b/client/src/pages/config/users/configman.jsx index 20e90bff1f8935bf6acec6c73bd31cb32857b90e..e3a57af4a276a32cacb63e77b749c063350dfc54 100644 --- a/client/src/pages/config/users/configman.jsx +++ b/client/src/pages/config/users/configman.jsx @@ -31,6 +31,7 @@ import { TwitterPicker // TODO: Remove circular deps import {SetPrimaryColor, SetSecondaryColor} from '../../../App'; +import { useClientInfos } from '../../../utils/hooks'; const ConfigManagement = () => { const [config, setConfig] = React.useState(null); @@ -38,6 +39,8 @@ const ConfigManagement = () => { const [openResartModal, setOpenRestartModal] = React.useState(false); const [uploadingBackground, setUploadingBackground] = React.useState(false); const [saveLabel, setSaveLabel] = React.useState("Save"); + const {role} = useClientInfos(); + const isAdmin = role === 1; function refresh() { API.config.get().then((res) => { @@ -62,9 +65,9 @@ const ConfigManagement = () => { refresh(); }}>Refresh - + }}>Restart Server} {config && <> @@ -186,7 +189,7 @@ const ConfigManagement = () => { {(formik) => (
- + {isAdmin && {formik.errors.submit && ( {formik.errors.submit} @@ -205,7 +208,13 @@ const ConfigManagement = () => { {saveLabel} - + } + + {!isAdmin &&
+ As you are not an admin, you can't edit the configuration. + This page is only here for visibility. + +
} @@ -331,6 +340,29 @@ const ConfigManagement = () => { formik.setFieldValue('PrimaryColor', colorRGB); SetPrimaryColor(colorRGB); }} + colors={[ + '#ab47bc', + '#4527a0', + '#FF6900', + '#FCB900', + '#7BDCB5', + '#00D084', + '#8ED1FC', + '#0693E3', + '#ABB8C3', + '#EB144C', + '#F78DA7', + '#9900EF', + '#FF0000', + '#FFC0CB', + '#20B2AA', + '#FFFF00', + '#8A2BE2', + '#A52A2A', + '#5F9EA0', + '#7FFF00', + '#D2691E' + ]} />
@@ -346,6 +378,29 @@ const ConfigManagement = () => { formik.setFieldValue('SecondaryColor', colorRGB); SetSecondaryColor(colorRGB); }} + colors={[ + '#ab47bc', + '#4527a0', + '#FF6900', + '#FCB900', + '#7BDCB5', + '#00D084', + '#8ED1FC', + '#0693E3', + '#ABB8C3', + '#EB144C', + '#F78DA7', + '#9900EF', + '#FF0000', + '#FFC0CB', + '#20B2AA', + '#FFFF00', + '#8A2BE2', + '#A52A2A', + '#5F9EA0', + '#7FFF00', + '#D2691E' + ]} /> @@ -627,7 +682,7 @@ const ConfigManagement = () => { - + {isAdmin && {formik.errors.submit && ( {formik.errors.submit} @@ -646,7 +701,7 @@ const ConfigManagement = () => { {saveLabel} - + }
)} diff --git a/client/src/pages/constellation/addDevice.jsx b/client/src/pages/constellation/addDevice.jsx index ea2aea84b5e6ccb00256bf987018442c2c9575ca..1000dbc6142efb1654dde9979817aae5ad85cfd2 100644 --- a/client/src/pages/constellation/addDevice.jsx +++ b/client/src/pages/constellation/addDevice.jsx @@ -1,5 +1,5 @@ // material-ui -import { Alert, Button, Stack, TextField } from '@mui/material'; +import { Alert, Button, InputLabel, OutlinedInput, Stack, TextField } from '@mui/material'; import Dialog from '@mui/material/Dialog'; import DialogActions from '@mui/material/DialogActions'; import DialogContent from '@mui/material/DialogContent'; @@ -15,6 +15,7 @@ import * as API from '../../api'; import { CosmosCheckbox, CosmosFormDivider, CosmosInputText, CosmosSelect } from '../config/users/formShortcuts'; import { DownloadFile } from '../../api/downloadButton'; import QRCode from 'qrcode'; +import { useClientInfos } from '../../utils/hooks'; const getDocker = (data, isCompose) => { let lighthouses = ''; @@ -73,10 +74,12 @@ docker run -d \\ } -const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { +const AddDeviceModal = ({ users, config, refreshConfig, devices }) => { const [openModal, setOpenModal] = useState(false); const [isDone, setIsDone] = useState(null); const canvasRef = React.useRef(null); + const {role, nickname} = useClientInfos(); + const isAdmin = role === 1; let firstIP = "192.168.201.2/24"; if (devices && devices.length > 0) { @@ -116,7 +119,7 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => { setOpenModal(false)}> { formik={formik} /> {!formik.values.isLighthouse && - { return [u.nickname, u.nickname] }) } - />} + /> : <> + Owner + + )} { }, []); return <> - {(config) ? <>
@@ -40,8 +39,6 @@ export const ConstellationDNS = () => { { }} onSubmit={(values) => { let newConfig = { ...config }; - newConfig.ConstellationConfig.DNS = values.Enabled; - newConfig.ConstellationConfig.DNSPort = values.Port; newConfig.ConstellationConfig.DNSFallback = values.Fallback; newConfig.ConstellationConfig.DNSBlockBlacklist = values.DNSBlockBlacklist; newConfig.ConstellationConfig.DNSAdditionalBlocklists = values.DNSAdditionalBlocklists; @@ -62,14 +57,19 @@ export const ConstellationDNS = () => { {(formik) => (
- - + This is a DNS that runs inside your Constellation network. It automatically + rewrites your domains DNS entries to be local to your network, and also allows you to do things like block ads + and trackers on all devices connected to your network. You can also add custom DNS entries to resolve to specific + IP addresses. This DNS server is only accessible from inside your network. + + When changing your DNS records, always use private mode on your browser and allow some times for various caches to expire. + DNS Blocklist URLs {formik.values.DNSAdditionalBlocklists.map((item, index) => ( diff --git a/client/src/pages/constellation/index.jsx b/client/src/pages/constellation/index.jsx index dea63ecbc59e63a996c69fdebe25583449ca29fe..6b76c754ec2ddf46c02ceefaeb6b27529c799b06 100644 --- a/client/src/pages/constellation/index.jsx +++ b/client/src/pages/constellation/index.jsx @@ -1,6 +1,6 @@ import * as React from 'react'; import MainCard from '../../components/MainCard'; -import { Chip, Divider, Stack, useMediaQuery } from '@mui/material'; +import { Alert, Chip, Divider, Stack, useMediaQuery } from '@mui/material'; import HostChip from '../../components/hostChip'; import { RouteMode, RouteSecurity } from '../../components/routeComponents'; import { getFaviconURL } from '../../utils/routes'; @@ -8,12 +8,16 @@ import * as API from '../../api'; import { CheckOutlined, ClockCircleOutlined, DashboardOutlined, DeleteOutlined, DownOutlined, LockOutlined, UpOutlined } from "@ant-design/icons"; import IsLoggedIn from '../../isLoggedIn'; import PrettyTabbedView from '../../components/tabbedView/tabbedView'; +import { useClientInfos } from '../../utils/hooks'; import { ConstellationVPN } from './vpn'; import { ConstellationDNS } from './dns'; const ConstellationIndex = () => { - return
+ const {role} = useClientInfos(); + const isAdmin = role === 1; + + return isAdmin ?
{ children: , path: 'dns' }, + { + title: 'Firewall', + children:
+ + Coming soon. This feature will allow you to open and close ports individually + on each device and decide who can access them. + +
, + }, + { + title: 'Unsafe Routes', + children:
+ + Coming soon. This feature will allow you to tunnel your traffic through + your devices to things outside of your constellation. + +
, + } ]}/> -
; +
: ; } export default ConstellationIndex; \ No newline at end of file diff --git a/client/src/pages/constellation/vpn.jsx b/client/src/pages/constellation/vpn.jsx index 65e7b9f209dfae20b0c90d47dbd94fe8a9ecdb95..c60c0afb602c64558e05f68f2b77981c769de558 100644 --- a/client/src/pages/constellation/vpn.jsx +++ b/client/src/pages/constellation/vpn.jsx @@ -15,6 +15,7 @@ import ApiModal from "../../components/apiModal"; import { isDomain } from "../../utils/indexs"; import ConfirmModal from "../../components/confirmModal"; import UploadButtons from "../../components/fileUpload"; +import { useClientInfos } from "../../utils/hooks"; const getDefaultConstellationHostname = (config) => { // if domain is set, use it @@ -26,17 +27,20 @@ const getDefaultConstellationHostname = (config) => { } export const ConstellationVPN = () => { - const [isAdmin, setIsAdmin] = useState(false); const [config, setConfig] = useState(null); const [users, setUsers] = useState(null); const [devices, setDevices] = useState(null); + const {role} = useClientInfos(); + const isAdmin = role === 1; const refreshConfig = async () => { let configAsync = await API.config.get(); setConfig(configAsync.data); - setIsAdmin(configAsync.isAdmin); setDevices((await API.constellation.list()).data || []); - setUsers((await API.users.list()).data || []); + if(isAdmin) + setUsers((await API.users.list()).data || []); + else + setUsers([]); }; useEffect(() => { @@ -61,7 +65,6 @@ export const ConstellationVPN = () => { } return <> - {(devices && config && users) ? <>
@@ -160,7 +163,7 @@ export const ConstellationVPN = () => { data={devices.filter((d) => !d.blocked)} getKey={(r) => r.deviceName} buttons={[ - , + , ]} columns={[ { diff --git a/client/src/pages/home/index.jsx b/client/src/pages/home/index.jsx index 253f497ef770423c4d0b72b2740c1d78fa6bcb04..99367c871ff0ab0cf41184a52f320116b8cf1217 100644 --- a/client/src/pages/home/index.jsx +++ b/client/src/pages/home/index.jsx @@ -12,6 +12,7 @@ import { getFullOrigin } from "../../utils/routes"; import IsLoggedIn from "../../isLoggedIn"; import { ServAppIcon } from "../../utils/servapp-icon"; import Chart from 'react-apexcharts'; +import { useClientInfos } from "../../utils/hooks"; export const HomeBackground = () => { @@ -87,6 +88,8 @@ const HomePage = () => { const theme = useTheme(); const isDark = theme.palette.mode === 'dark'; const isMd = useMediaQuery(theme.breakpoints.up('md')); + const {role} = useClientInfos(); + const isAdmin = role === 1; const blockStyle = { margin: 0, @@ -112,9 +115,13 @@ const HomePage = () => { } const refreshConfig = () => { - API.docker.list().then((res) => { - setServApps(res.data); - }); + if(isAdmin) { + API.docker.list().then((res) => { + setServApps(res.data); + }); + } else { + setServApps([]); + } API.config.get().then((res) => { setConfig(res.data); }); @@ -213,20 +220,20 @@ const HomePage = () => { - {coStatus && !coStatus.database && ( + {isAdmin && coStatus && !coStatus.database && ( No Database is setup for Cosmos! User Management and Authentication will not work.
You can either setup the database, or disable user management in the configuration panel.
)} - {coStatus && coStatus.letsencrypt && ( + {isAdmin && coStatus && coStatus.letsencrypt && ( You have enabled Let's Encrypt for automatic HTTPS Certificate. You need to provide the configuration with an email address to use for Let's Encrypt in the configs. )} - {coStatus && coStatus.LetsEncryptErrors && coStatus.LetsEncryptErrors.length > 0 && ( + {isAdmin && coStatus && coStatus.LetsEncryptErrors && coStatus.LetsEncryptErrors.length > 0 && ( There are errors with your Let's Encrypt configuration or one of your routes, please fix them as soon as possible: {coStatus.LetsEncryptErrors.map((err) => { @@ -235,25 +242,25 @@ const HomePage = () => { )} - {coStatus && coStatus.newVersionAvailable && ( + {isAdmin && coStatus && coStatus.newVersionAvailable && ( A new version of Cosmos is available! Please update to the latest version to get the latest features and bug fixes. )} - {coStatus && coStatus.needsRestart && ( + {isAdmin && coStatus && coStatus.needsRestart && ( You have made changes to the configuration that require a restart to take effect. Please restart Cosmos to apply the changes. )} - {coStatus && coStatus.domain && ( + {isAdmin && coStatus && coStatus.domain && ( You are using localhost or 0.0.0.0 as a hostname in the configuration. It is recommended that you use a domain name or an IP instead. )} - {coStatus && !coStatus.docker && ( + {isAdmin && coStatus && !coStatus.docker && ( Docker is not connected! Please check your docker connection.
Did you forget to add
-v /var/run/docker.sock:/var/run/docker.sock
to your docker run command?
diff --git a/client/src/pages/market/listing.jsx b/client/src/pages/market/listing.jsx index 6a8d6e79f378f6c34b95275c46d4c43c41d429a1..df430d3ac04705f7d63f94207fcedac4a24712d0 100644 --- a/client/src/pages/market/listing.jsx +++ b/client/src/pages/market/listing.jsx @@ -12,6 +12,7 @@ import { Link as LinkMUI } from '@mui/material' import DockerComposeImport from '../servapps/containers/docker-compose'; import { AppstoreAddOutlined, SearchOutlined } from "@ant-design/icons"; import ResponsiveButton from "../../components/responseiveButton"; +import { useClientInfos } from "../../utils/hooks"; function Screenshots({ screenshots }) { return screenshots.length > 1 ? ( @@ -23,17 +24,17 @@ function Screenshots({ screenshots }) { : } -function Showcases({ showcase, isDark }) { +function Showcases({ showcase, isDark, isAdmin }) { return ( { - showcase.map((item, i) => ) + showcase.map((item, i) => ) } ) } -function ShowcasesItem({ isDark, item }) { +function ShowcasesItem({ isDark, item, isAdmin }) { return (

-
+ {isAdmin &&
-
+
} @@ -110,6 +111,8 @@ const MarketPage = () => { const isDark = theme.palette.mode === 'dark'; const { appName, appStore } = useParams(); const [search, setSearch] = useState(""); + const {role} = useClientInfos(); + const isAdmin = role === 1; const backgroundStyle = isDark ? { backgroundColor: 'rgb(0,0,0)', @@ -178,7 +181,7 @@ const MarketPage = () => {
- +
@@ -202,9 +205,9 @@ const MarketPage = () => {
-
+ {isAdmin &&
-
+
}
} @@ -223,7 +226,7 @@ const MarketPage = () => { size={100} /> } - {showcase && showcase.length > 0 && } + {showcase && showcase.length > 0 && }
= 16.3.0" + } + }, "node_modules/react-copy-to-clipboard": { "version": "5.1.0", "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz", @@ -10368,6 +10387,15 @@ "node": ">=4" } }, + "node_modules/universal-cookie": { + "version": "6.1.1", + "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-6.1.1.tgz", + "integrity": "sha512-33S9x3CpdUnnjwTNs2Fgc41WGve2tdLtvaK2kPSbZRc5pGpz2vQFbRWMxlATsxNNe/Cy8SzmnmbuBM85jpZPtA==", + "dependencies": { + "@types/cookie": "^0.5.1", + "cookie": "^0.5.0" + } + }, "node_modules/unpipe": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz", diff --git a/package.json b/package.json index 6a39eec199f50498987a7b64c65ce80a625d7e0c..6d59db90ab3f134a3aaeb508a48d61b12ddb7fc3 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "cosmos-server", - "version": "0.10.0-unstable16", + "version": "0.10.0-unstable17", "description": "", "main": "test-server.js", "bugs": { @@ -36,6 +36,7 @@ "react": "^18.2.0", "react-apexcharts": "^1.4.0", "react-color": "^2.19.3", + "react-cookie": "^6.1.1", "react-copy-to-clipboard": "^5.1.0", "react-device-detect": "^2.2.2", "react-dom": "^18.2.0", diff --git a/src/constellation/DNS.go b/src/constellation/DNS.go index c7f23ac58630e8af94dd13e073ecbe3fb1dfff32..40864cd8f861aa5cc00188c2a86f47c5e700019e 100644 --- a/src/constellation/DNS.go +++ b/src/constellation/DNS.go @@ -176,7 +176,7 @@ func InitDNS() { utils.Log("Loaded " + strconv.Itoa(len(DNSBlacklist)) + " domains") } - if(config.ConstellationConfig.DNS) { + if(!config.ConstellationConfig.DNSDisabled) { go (func() { dns.HandleFunc(".", handleDNSRequest) server := &dns.Server{Addr: ":" + DNSPort, Net: "udp"} diff --git a/src/constellation/api_devices_block.go b/src/constellation/api_devices_block.go index 9465c764799e5a71bea5d3170c0af96963849798..14568de3473152e3ea0be2f88aa8bf6a0591817c 100644 --- a/src/constellation/api_devices_block.go +++ b/src/constellation/api_devices_block.go @@ -39,6 +39,8 @@ func DeviceBlock(w http.ResponseWriter, req *http.Request) { return } + utils.Log("ConstellationDeviceBlocking: Blocking Device " + deviceName) + c, errCo := utils.GetCollection(utils.GetRootAppId(), "devices") if errCo != nil { utils.Error("Database Connect", errCo) diff --git a/src/constellation/api_devices_config.go b/src/constellation/api_devices_config.go new file mode 100644 index 0000000000000000000000000000000000000000..b9fe8f5531b221596def25c6114ca77c728c436e --- /dev/null +++ b/src/constellation/api_devices_config.go @@ -0,0 +1,41 @@ +package constellation + +// import ( +// "net/http" +// "encoding/json" +// "math/rand" +// "time" +// "net" + +// "github.com/azukaar/cosmos-server/src/utils" +// ) + +// func DeviceConfig(w http.ResponseWriter, req *http.Request) { +// time.Sleep(time.Duration(rand.Float64()*2)*time.Second) + +// if(req.Method == "GET") { + +// ip, _, err := net.SplitHostPort(req.RemoteAddr) +// if err != nil { +// http.Error(w, "Invalid request", http.StatusBadRequest) +// return +// } + +// // get authorization header +// auth := req.Header.Get("Authorization") +// if auth == "" { +// http.Error(w, "Unauthorized", http.StatusUnauthorized) +// return +// } + +// // remove "Bearer " from auth header +// auth = strings.Replace(auth, "Bearer ", "", 1) + + + +// } else { +// utils.Error("DeviceConfig: Method not allowed" + req.Method, nil) +// utils.HTTPError(w, "Method not allowed", http.StatusMethodNotAllowed, "HTTP001") +// return +// } +// } \ No newline at end of file diff --git a/src/constellation/api_devices_create.go b/src/constellation/api_devices_create.go index bc817542817f5dbcc86e846d6621f60bb5358e48..995669bcceccdb4045be368c45752da5193451ea 100644 --- a/src/constellation/api_devices_create.go +++ b/src/constellation/api_devices_create.go @@ -44,11 +44,14 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { nickname := utils.Sanitize(request.Nickname) deviceName := utils.Sanitize(request.DeviceName) + APIKey := utils.GenerateRandomString(32) if utils.AdminOrItselfOnly(w, req, nickname) != nil { return } + utils.Log("ConstellationDeviceCreation: Creating Device " + deviceName) + c, errCo := utils.GetCollection(utils.GetRootAppId(), "devices") if errCo != nil { utils.Error("Database Connect", errCo) @@ -100,6 +103,8 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { "PublicHostname": request.PublicHostname, "Port": request.Port, "Fingerprint": fingerprint, + "APIKey": APIKey, + "Blocked": false, }) if err3 != nil { @@ -123,7 +128,7 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { } // read configYml from config/nebula.yml - configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml", capki, cert, key, utils.ConstellationDevice{ + configYml, err := getYAMLClientConfig(deviceName, utils.CONFIGFOLDER + "nebula.yml", capki, cert, key, APIKey, utils.ConstellationDevice{ Nickname: nickname, DeviceName: deviceName, PublicKey: key, @@ -132,6 +137,7 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) { IsRelay: request.IsRelay, PublicHostname: request.PublicHostname, Port: request.Port, + APIKey: APIKey, }) if err != nil { diff --git a/src/constellation/api_nebula_connect.go b/src/constellation/api_nebula_connect.go index 8972c74aff9acef248c23ae9c78225139052de7f..2053db420b6a4f0db058a52af14626a1d86ccb4d 100644 --- a/src/constellation/api_nebula_connect.go +++ b/src/constellation/api_nebula_connect.go @@ -26,7 +26,7 @@ func API_ConnectToExisting(w http.ResponseWriter, req *http.Request) { config := utils.ReadConfigFromFile() config.ConstellationConfig.Enabled = true config.ConstellationConfig.SlaveMode = true - config.ConstellationConfig.DNS = false + config.ConstellationConfig.DNSDisabled = false // ConstellationHostname = // output utils.CONFIGFOLDER + "nebula.yml" diff --git a/src/constellation/nebula.go b/src/constellation/nebula.go index e4cc6e9e54472ded23525aa54cb5dce22a90fd5c..fa4fa7bc5f45143a61a181cd71dc7e289293419f 100644 --- a/src/constellation/nebula.go +++ b/src/constellation/nebula.go @@ -235,7 +235,7 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st return nil } -func getYAMLClientConfig(name, configPath, capki, cert, key string, device utils.ConstellationDevice) (string, error) { +func getYAMLClientConfig(name, configPath, capki, cert, key, APIKey string, device utils.ConstellationDevice) (string, error) { utils.Log("Exporting YAML config for " + name + " with file " + configPath) // Read the YAML config file @@ -321,9 +321,11 @@ func getYAMLClientConfig(name, configPath, capki, cert, key string, device utils return "", errors.New("listen not found in nebula.yml") } - configMap["deviceName"] = name - configMap["local_dns_overwrite"] = "192.168.201.1" - configMap["public_hostname"] = device.PublicHostname + configMap["constellation_device_name"] = name + configMap["constellation_local_dns_overwrite"] = true + configMap["constellation_local_dns_overwrite_address"] = "192.168.201.1" + configMap["constellation_public_hostname"] = device.PublicHostname + configMap["constellation_api_key"] = APIKey // export configMap as YML yamlData, err = yaml.Marshal(configMap) diff --git a/src/market/index.go b/src/market/index.go index 7081b74d75482f05848623fc32c092b0dae65410..ee5d4afc836e707c8167c34a44812d5f71639fca 100644 --- a/src/market/index.go +++ b/src/market/index.go @@ -12,7 +12,7 @@ type marketGetResult struct { } func MarketGet(w http.ResponseWriter, req *http.Request) { - if utils.AdminOnly(w, req) != nil { + if utils.LoggedInOnly(w, req) != nil { return } @@ -22,7 +22,6 @@ func MarketGet(w http.ResponseWriter, req *http.Request) { return } - // return the first 10 results of each market marketGetResult := marketGetResult{ All: make(map[string]interface{}), Showcase: []appDefinition{}, diff --git a/src/user/token.go b/src/user/token.go index dd467bb1aebb2bab8075463b1560d40a7af4cdea..ad30ab312d141e3609bdc8792a9a19d124142316 100644 --- a/src/user/token.go +++ b/src/user/token.go @@ -5,6 +5,7 @@ import ( "github.com/azukaar/cosmos-server/src/utils" "github.com/golang-jwt/jwt" "errors" + "strconv" "strings" "time" "encoding/json" @@ -189,13 +190,25 @@ func logOutUser(w http.ResponseWriter, req *http.Request) { HttpOnly: true, } + clientCookie := http.Cookie{ + Name: "client-infos", + Value: "{}", + Expires: time.Now().Add(-time.Hour * 24 * 365), + Path: "/", + Secure: utils.IsHTTPS, + HttpOnly: false, + } + if reqHostNoPort == "localhost" || reqHostNoPort == "0.0.0.0" { cookie.Domain = "" + clientCookie.Domain = "" } else { cookie.Domain = "." + reqHostNoPort + clientCookie.Domain = "." + reqHostNoPort } http.SetCookie(w, &cookie) + http.SetCookie(w, &clientCookie) // TODO: logout every other device if asked by increasing passwordcycle } @@ -254,13 +267,24 @@ func SendUserToken(w http.ResponseWriter, req *http.Request, user utils.User, mf HttpOnly: true, } + clientCookie := http.Cookie{ + Name: "client-infos", + Value: user.Nickname + "," + strconv.Itoa(int(user.Role)), + Expires: expiration, + Path: "/", + Secure: utils.IsHTTPS, + HttpOnly: false, + } + utils.Log("UserLogin: Setting cookie for " + reqHostNoPort) if reqHostNoPort == "localhost" || reqHostNoPort == "0.0.0.0" { cookie.Domain = "" + clientCookie.Domain = "" } else { if utils.IsValidHostname(reqHostNoPort) { cookie.Domain = "." + reqHostNoPort + clientCookie.Domain = "." + reqHostNoPort } else { utils.Error("UserLogin: Invalid hostname", nil) utils.HTTPError(w, "User Logging Error", http.StatusInternalServerError, "UL001") @@ -269,4 +293,5 @@ func SendUserToken(w http.ResponseWriter, req *http.Request, user utils.User, mf } http.SetCookie(w, &cookie) + http.SetCookie(w, &clientCookie) } \ No newline at end of file diff --git a/src/utils/types.go b/src/utils/types.go index 0a6bcc792548a3d574f70e63a0b15333cc90b586..0c187d8a81c0047bf1516ad31dc207c9be2aef41 100644 --- a/src/utils/types.go +++ b/src/utils/types.go @@ -213,7 +213,7 @@ type ConstellationConfig struct { Enabled bool SlaveMode bool PrivateNode bool - DNS bool + DNSDisabled bool DNSPort string DNSFallback string DNSBlockBlacklist bool @@ -239,6 +239,7 @@ type ConstellationDevice struct { Port string `json:"port"` Blocked bool `json:"blocked"` Fingerprint string `json:"fingerprint"` + APIKey string `json:"-"` } type NebulaFirewallRule struct { diff --git a/src/utils/utils.go b/src/utils/utils.go index 1a32bed820ddf6df1666b9ba926e54a5f69023b8..cf5b0b388ccfc596d59265674e2d78067e14b4e0 100644 --- a/src/utils/utils.go +++ b/src/utils/utils.go @@ -64,7 +64,7 @@ var DefaultConfig = Config{ }, ConstellationConfig: ConstellationConfig{ Enabled: false, - DNS: true, + DNSDisabled: false, DNSFallback: "8.8.8.8:53", DNSAdditionalBlocklists: []string{ "https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt",