Browse Source

[release] v0.10.0-unstable17

Yann Stepienik 1 year ago
parent
commit
c7dda3db6c

+ 44 - 18
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
                                  Apache License
                            Version 2.0, January 2004
                            Version 2.0, January 2004

+ 2 - 0
changelog.md

@@ -7,6 +7,8 @@
  - Added Constellation
  - Added Constellation
  - DNS Challenge is now used for all certificates when enabled
  - DNS Challenge is now used for all certificates when enabled
  - Rework headers for better compatibility
  - Rework headers for better compatibility
+ - Improve experience for non-admin users
+ - Fix bug with redirect on logout
  
  
 ## Version 0.9.20 - 0.9.21
 ## Version 0.9.20 - 0.9.21
  - Add option to disable CORS hardening (with empty value)
  - Add option to disable CORS hardening (with empty value)

+ 1 - 1
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)
 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.
 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.
 
 

+ 1 - 1
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 }}
             sx={{ mb: drawerOpen ? 1.5 : 0, py: 0, zIndex: 0 }}
         >
         >
-            {navCollapse}
+        {navCollapse}
         </List>
         </List>
     );
     );
 };
 };

+ 7 - 0
client/src/layout/MainLayout/Drawer/DrawerContent/Navigation/NavItem.jsx

@@ -9,6 +9,7 @@ import { Avatar, Chip, ListItemButton, ListItemIcon, ListItemText, Typography }
 
 
 // project import
 // project import
 import { activeItem } from '../../../../../store/reducers/menu';
 import { activeItem } from '../../../../../store/reducers/menu';
+import { useClientInfos } from '../../../../../utils/hooks';
 
 
 // ==============================|| NAVIGATION - LIST ITEM ||============================== //
 // ==============================|| NAVIGATION - LIST ITEM ||============================== //
 
 
@@ -17,6 +18,12 @@ const NavItem = ({ item, level }) => {
     const dispatch = useDispatch();
     const dispatch = useDispatch();
     const menu = useSelector((state) => state.menu);
     const menu = useSelector((state) => state.menu);
     const { drawerOpen, openItem } = menu;
     const { drawerOpen, openItem } = menu;
+    const {role} = useClientInfos();
+    const isAdmin = role === 1;
+
+    if (item.adminOnly && !isAdmin) {
+        return null;
+    }
 
 
     let itemTarget = '_self';
     let itemTarget = '_self';
     if (item.target) {
     if (item.target) {

+ 4 - 1
client/src/menu-items/pages.jsx

@@ -20,7 +20,8 @@ const pages = {
             title: 'ServApps',
             title: 'ServApps',
             type: 'item',
             type: 'item',
             url: '/cosmos-ui/servapps',
             url: '/cosmos-ui/servapps',
-            icon: AppstoreOutlined
+            icon: AppstoreOutlined,
+            adminOnly: true
         },
         },
         {
         {
             id: 'url',
             id: 'url',
@@ -43,6 +44,7 @@ const pages = {
             type: 'item',
             type: 'item',
             url: '/cosmos-ui/config-users',
             url: '/cosmos-ui/config-users',
             icon: icons.ProfileOutlined,
             icon: icons.ProfileOutlined,
+            adminOnly: true
         },
         },
         {
         {
             id: 'openid',
             id: 'openid',
@@ -50,6 +52,7 @@ const pages = {
             type: 'item',
             type: 'item',
             url: '/cosmos-ui/openid-manage',
             url: '/cosmos-ui/openid-manage',
             icon: PicLeftOutlined,
             icon: PicLeftOutlined,
+            adminOnly: true
         },
         },
         {
         {
             id: 'config',
             id: 'config',

+ 1 - 1
client/src/pages/authentication/Logoff.jsx

@@ -9,7 +9,7 @@ import AuthWrapper from './AuthWrapper';
 import { useEffect } from 'react';
 import { useEffect } from 'react';
 
 
 import * as API from '../../api';
 import * as API from '../../api';
-import { redirectTo } from '../../utils/indexs';
+import { redirectTo, redirectToLocal } from '../../utils/indexs';
 
 
 // ================================|| REGISTER ||================================ //
 // ================================|| REGISTER ||================================ //
 
 

+ 61 - 6
client/src/pages/config/users/configman.jsx

@@ -31,6 +31,7 @@ import { TwitterPicker
 
 
  // TODO: Remove circular deps
  // TODO: Remove circular deps
  import {SetPrimaryColor, SetSecondaryColor} from '../../../App';
  import {SetPrimaryColor, SetSecondaryColor} from '../../../App';
+import { useClientInfos } from '../../../utils/hooks';
 
 
 const ConfigManagement = () => {
 const ConfigManagement = () => {
   const [config, setConfig] = React.useState(null);
   const [config, setConfig] = React.useState(null);
@@ -38,6 +39,8 @@ const ConfigManagement = () => {
   const [openResartModal, setOpenRestartModal] = React.useState(false);
   const [openResartModal, setOpenRestartModal] = React.useState(false);
   const [uploadingBackground, setUploadingBackground] = React.useState(false);
   const [uploadingBackground, setUploadingBackground] = React.useState(false);
   const [saveLabel, setSaveLabel] = React.useState("Save");
   const [saveLabel, setSaveLabel] = React.useState("Save");
+  const {role} = useClientInfos();
+  const isAdmin = role === 1;
 
 
   function refresh() {
   function refresh() {
     API.config.get().then((res) => {
     API.config.get().then((res) => {
@@ -62,9 +65,9 @@ const ConfigManagement = () => {
           refresh();
           refresh();
       }}>Refresh</Button>
       }}>Refresh</Button>
 
 
-      <Button variant="outlined" color="primary" startIcon={<SyncOutlined />} onClick={() => {
+      {isAdmin && <Button variant="outlined" color="primary" startIcon={<SyncOutlined />} onClick={() => {
           setOpenRestartModal(true);
           setOpenRestartModal(true);
-      }}>Restart Server</Button>
+      }}>Restart Server</Button>}
     </Stack>
     </Stack>
     
     
     {config && <>
     {config && <>
@@ -186,7 +189,7 @@ const ConfigManagement = () => {
         {(formik) => (
         {(formik) => (
           <form noValidate onSubmit={formik.handleSubmit}>
           <form noValidate onSubmit={formik.handleSubmit}>
             <Stack spacing={3}>
             <Stack spacing={3}>
-            <MainCard>
+            {isAdmin && <MainCard>
                 {formik.errors.submit && (
                 {formik.errors.submit && (
                   <Grid item xs={12}>
                   <Grid item xs={12}>
                     <FormHelperText error>{formik.errors.submit}</FormHelperText>
                     <FormHelperText error>{formik.errors.submit}</FormHelperText>
@@ -205,7 +208,13 @@ const ConfigManagement = () => {
                       {saveLabel}
                       {saveLabel}
                     </LoadingButton>
                     </LoadingButton>
                 </Grid>
                 </Grid>
-              </MainCard>
+              </MainCard>}
+
+              {!isAdmin && <div>
+                <Alert severity="warning">As you are not an admin, you can't edit the configuration.
+                This page is only here for visibility. 
+                </Alert>
+              </div>} 
 
 
               <MainCard title="General">
               <MainCard title="General">
                 <Grid container spacing={3}>
                 <Grid container spacing={3}>
@@ -331,6 +340,29 @@ const ConfigManagement = () => {
                           formik.setFieldValue('PrimaryColor', colorRGB);
                           formik.setFieldValue('PrimaryColor', colorRGB);
                           SetPrimaryColor(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' 
+                        ]}
                       />
                       />
                     </Stack>
                     </Stack>
                   </Grid>
                   </Grid>
@@ -346,6 +378,29 @@ const ConfigManagement = () => {
                           formik.setFieldValue('SecondaryColor', colorRGB);
                           formik.setFieldValue('SecondaryColor', colorRGB);
                           SetSecondaryColor(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' 
+                        ]}
                       />
                       />
                     </Stack>
                     </Stack>
                   </Grid>
                   </Grid>
@@ -627,7 +682,7 @@ const ConfigManagement = () => {
                 </Grid>
                 </Grid>
               </MainCard>
               </MainCard>
 
 
-              <MainCard>
+              {isAdmin && <MainCard>
                 {formik.errors.submit && (
                 {formik.errors.submit && (
                   <Grid item xs={12}>
                   <Grid item xs={12}>
                     <FormHelperText error>{formik.errors.submit}</FormHelperText>
                     <FormHelperText error>{formik.errors.submit}</FormHelperText>
@@ -646,7 +701,7 @@ const ConfigManagement = () => {
                       {saveLabel}
                       {saveLabel}
                     </LoadingButton>
                     </LoadingButton>
                 </Grid>
                 </Grid>
-              </MainCard>
+              </MainCard>}
             </Stack>
             </Stack>
           </form>
           </form>
         )}
         )}

+ 18 - 5
client/src/pages/constellation/addDevice.jsx

@@ -1,5 +1,5 @@
 // material-ui
 // 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 Dialog from '@mui/material/Dialog';
 import DialogActions from '@mui/material/DialogActions';
 import DialogActions from '@mui/material/DialogActions';
 import DialogContent from '@mui/material/DialogContent';
 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 { CosmosCheckbox, CosmosFormDivider, CosmosInputText, CosmosSelect } from '../config/users/formShortcuts';
 import { DownloadFile } from '../../api/downloadButton';
 import { DownloadFile } from '../../api/downloadButton';
 import QRCode from 'qrcode';
 import QRCode from 'qrcode';
+import { useClientInfos } from '../../utils/hooks';
 
 
 const getDocker = (data, isCompose) => {
 const getDocker = (data, isCompose) => {
   let lighthouses = '';
   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 [openModal, setOpenModal] = useState(false);
   const [isDone, setIsDone] = useState(null);
   const [isDone, setIsDone] = useState(null);
   const canvasRef = React.useRef(null);
   const canvasRef = React.useRef(null);
+  const {role, nickname} = useClientInfos();
+  const isAdmin = role === 1;
 
 
   let firstIP = "192.168.201.2/24";
   let firstIP = "192.168.201.2/24";
   if (devices && devices.length > 0) { 
   if (devices && devices.length > 0) { 
@@ -116,7 +119,7 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => {
     <Dialog open={openModal} onClose={() => setOpenModal(false)}>
     <Dialog open={openModal} onClose={() => setOpenModal(false)}>
       <Formik
       <Formik
         initialValues={{
         initialValues={{
-          nickname: users[0].nickname,
+          nickname: nickname,
           deviceName: '',
           deviceName: '',
           ip: firstIP,
           ip: firstIP,
           publicKey: '',
           publicKey: '',
@@ -197,7 +200,7 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => {
                     formik={formik}
                     formik={formik}
                   />
                   />
                   {!formik.values.isLighthouse &&
                   {!formik.values.isLighthouse &&
-                    <CosmosSelect
+                    (isAdmin ? <CosmosSelect
                       name="nickname"
                       name="nickname"
                       label="Owner"
                       label="Owner"
                       formik={formik}
                       formik={formik}
@@ -207,7 +210,17 @@ const AddDeviceModal = ({ users, config, isAdmin, refreshConfig, devices }) => {
                           return [u.nickname, u.nickname]
                           return [u.nickname, u.nickname]
                         })
                         })
                       }
                       }
-                    />}
+                    /> : <>
+                      <InputLabel>Owner</InputLabel>
+                      <OutlinedInput
+                        fullWidth
+                        multiline
+                        value={nickname}
+                        variant="outlined"
+                        size="small"
+                        disabled
+                      />
+                    </>)}
 
 
                     <CosmosInputText
                     <CosmosInputText
                       name="deviceName"
                       name="deviceName"

+ 7 - 7
client/src/pages/constellation/dns.jsx

@@ -31,7 +31,6 @@ export const ConstellationDNS = () => {
   }, []);
   }, []);
 
 
   return <>
   return <>
-    <IsLoggedIn />
     {(config) ? <>
     {(config) ? <>
       <Stack spacing={2} style={{maxWidth: "1000px"}}>
       <Stack spacing={2} style={{maxWidth: "1000px"}}>
       <div>
       <div>
@@ -40,8 +39,6 @@ export const ConstellationDNS = () => {
 
 
           <Formik
           <Formik
             initialValues={{
             initialValues={{
-              Enabled: config.ConstellationConfig.DNS,
-              Port: config.ConstellationConfig.DNSPort,
               Fallback: config.ConstellationConfig.DNSFallback,
               Fallback: config.ConstellationConfig.DNSFallback,
               DNSBlockBlacklist: config.ConstellationConfig.DNSBlockBlacklist,
               DNSBlockBlacklist: config.ConstellationConfig.DNSBlockBlacklist,
               DNSAdditionalBlocklists: config.ConstellationConfig.DNSAdditionalBlocklists,
               DNSAdditionalBlocklists: config.ConstellationConfig.DNSAdditionalBlocklists,
@@ -49,8 +46,6 @@ export const ConstellationDNS = () => {
             }}
             }}
             onSubmit={(values) => {
             onSubmit={(values) => {
               let newConfig = { ...config };
               let newConfig = { ...config };
-              newConfig.ConstellationConfig.DNS = values.Enabled;
-              newConfig.ConstellationConfig.DNSPort = values.Port;
               newConfig.ConstellationConfig.DNSFallback = values.Fallback;
               newConfig.ConstellationConfig.DNSFallback = values.Fallback;
               newConfig.ConstellationConfig.DNSBlockBlacklist = values.DNSBlockBlacklist;
               newConfig.ConstellationConfig.DNSBlockBlacklist = values.DNSBlockBlacklist;
               newConfig.ConstellationConfig.DNSAdditionalBlocklists = values.DNSAdditionalBlocklists;
               newConfig.ConstellationConfig.DNSAdditionalBlocklists = values.DNSAdditionalBlocklists;
@@ -62,14 +57,19 @@ export const ConstellationDNS = () => {
             {(formik) => (
             {(formik) => (
               <form onSubmit={formik.handleSubmit}>
               <form onSubmit={formik.handleSubmit}>
                 <Stack spacing={2}>        
                 <Stack spacing={2}>        
-                  <CosmosCheckbox formik={formik} name="Enabled" label="Constellation DNS Server Enabled" />
-                  <CosmosInputText formik={formik} name="Port" label="DNS Port" />
+                  <Alert severity="info">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.</Alert>
+
                   <CosmosInputText formik={formik} name="Fallback" label="DNS Fallback" placeholder={'8.8.8.8:53'} />
                   <CosmosInputText formik={formik} name="Fallback" label="DNS Fallback" placeholder={'8.8.8.8:53'} />
                   
                   
                   <CosmosFormDivider title={"DNS Blocklists"} />
                   <CosmosFormDivider title={"DNS Blocklists"} />
 
 
                   <CosmosCheckbox formik={formik} name="DNSBlockBlacklist" label="Use Blacklists to block domains" />
                   <CosmosCheckbox formik={formik} name="DNSBlockBlacklist" label="Use Blacklists to block domains" />
 
 
+                  <Alert severity="warning">When changing your DNS records, always use private mode on your browser and allow some times for various caches to expire.</Alert>
+
                   <InputLabel>DNS Blocklist URLs</InputLabel>
                   <InputLabel>DNS Blocklist URLs</InputLabel>
                   {formik.values.DNSAdditionalBlocklists.map((item, index) => (
                   {formik.values.DNSAdditionalBlocklists.map((item, index) => (
                     <Stack direction={"row"} spacing={2} key={`DNSAdditionalBlocklists${item}`} width={"100%"}>
                     <Stack direction={"row"} spacing={2} key={`DNSAdditionalBlocklists${item}`} width={"100%"}>

+ 25 - 3
client/src/pages/constellation/index.jsx

@@ -1,6 +1,6 @@
 import * as React from 'react';
 import * as React from 'react';
 import MainCard from '../../components/MainCard';
 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 HostChip from '../../components/hostChip';
 import { RouteMode, RouteSecurity } from '../../components/routeComponents';
 import { RouteMode, RouteSecurity } from '../../components/routeComponents';
 import { getFaviconURL } from '../../utils/routes';
 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 { CheckOutlined, ClockCircleOutlined, DashboardOutlined, DeleteOutlined, DownOutlined, LockOutlined, UpOutlined } from "@ant-design/icons";
 import IsLoggedIn from '../../isLoggedIn';
 import IsLoggedIn from '../../isLoggedIn';
 import PrettyTabbedView from '../../components/tabbedView/tabbedView';
 import PrettyTabbedView from '../../components/tabbedView/tabbedView';
+import { useClientInfos } from '../../utils/hooks';
 
 
 import { ConstellationVPN } from './vpn';
 import { ConstellationVPN } from './vpn';
 import { ConstellationDNS } from './dns';
 import { ConstellationDNS } from './dns';
 
 
 const ConstellationIndex = () => {
 const ConstellationIndex = () => {
-  return <div>
+  const {role} = useClientInfos();
+  const isAdmin = role === 1;
+
+  return isAdmin ? <div>
     <IsLoggedIn />
     <IsLoggedIn />
     
     
     <PrettyTabbedView path="/cosmos-ui/constellation/:tab" tabs={[
     <PrettyTabbedView path="/cosmos-ui/constellation/:tab" tabs={[
@@ -27,9 +31,27 @@ const ConstellationIndex = () => {
           children: <ConstellationDNS />,
           children: <ConstellationDNS />,
           path: 'dns'
           path: 'dns'
         },
         },
+        {
+          title: 'Firewall',
+          children: <div>
+            <Alert severity="info">
+              Coming soon. This feature will allow you to open and close ports individually
+              on each device and decide who can access them.
+            </Alert>
+          </div>,
+        },
+        {
+          title: 'Unsafe Routes',
+          children: <div>
+            <Alert severity="info">
+              Coming soon. This feature will allow you to tunnel your traffic through
+              your devices to things outside of your constellation.
+            </Alert>
+          </div>,
+        }
       ]}/>
       ]}/>
 
 
-  </div>;
+  </div> : <ConstellationVPN />;
 }
 }
 
 
 export default ConstellationIndex;
 export default ConstellationIndex;

+ 8 - 5
client/src/pages/constellation/vpn.jsx

@@ -15,6 +15,7 @@ import ApiModal from "../../components/apiModal";
 import { isDomain } from "../../utils/indexs";
 import { isDomain } from "../../utils/indexs";
 import ConfirmModal from "../../components/confirmModal";
 import ConfirmModal from "../../components/confirmModal";
 import UploadButtons from "../../components/fileUpload";
 import UploadButtons from "../../components/fileUpload";
+import { useClientInfos } from "../../utils/hooks";
 
 
 const getDefaultConstellationHostname = (config) => {
 const getDefaultConstellationHostname = (config) => {
   // if domain is set, use it
   // if domain is set, use it
@@ -26,17 +27,20 @@ const getDefaultConstellationHostname = (config) => {
 }
 }
 
 
 export const ConstellationVPN = () => {
 export const ConstellationVPN = () => {
-  const [isAdmin, setIsAdmin] = useState(false);
   const [config, setConfig] = useState(null);
   const [config, setConfig] = useState(null);
   const [users, setUsers] = useState(null);
   const [users, setUsers] = useState(null);
   const [devices, setDevices] = useState(null);
   const [devices, setDevices] = useState(null);
+  const {role} = useClientInfos();
+  const isAdmin = role === 1;
 
 
   const refreshConfig = async () => {
   const refreshConfig = async () => {
     let configAsync = await API.config.get();
     let configAsync = await API.config.get();
     setConfig(configAsync.data);
     setConfig(configAsync.data);
-    setIsAdmin(configAsync.isAdmin);
     setDevices((await API.constellation.list()).data || []);
     setDevices((await API.constellation.list()).data || []);
-    setUsers((await API.users.list()).data || []);
+    if(isAdmin)
+      setUsers((await API.users.list()).data || []);
+    else 
+      setUsers([]);
   };
   };
 
 
   useEffect(() => {
   useEffect(() => {
@@ -61,7 +65,6 @@ export const ConstellationVPN = () => {
   }
   }
 
 
   return <>
   return <>
-    <IsLoggedIn />
     {(devices && config && users) ? <>
     {(devices && config && users) ? <>
       <Stack spacing={2} style={{maxWidth: "1000px"}}>
       <Stack spacing={2} style={{maxWidth: "1000px"}}>
       <div>
       <div>
@@ -160,7 +163,7 @@ export const ConstellationVPN = () => {
           data={devices.filter((d) => !d.blocked)}
           data={devices.filter((d) => !d.blocked)}
           getKey={(r) => r.deviceName}
           getKey={(r) => r.deviceName}
           buttons={[
           buttons={[
-            <AddDeviceModal isAdmin={isAdmin} users={users} config={config} refreshConfig={refreshConfig} devices={devices} />,
+            <AddDeviceModal users={users} config={config} refreshConfig={refreshConfig} devices={devices}/>,
           ]}
           ]}
           columns={[
           columns={[
               {
               {

+ 17 - 10
client/src/pages/home/index.jsx

@@ -12,6 +12,7 @@ import { getFullOrigin } from "../../utils/routes";
 import IsLoggedIn from "../../isLoggedIn";
 import IsLoggedIn from "../../isLoggedIn";
 import { ServAppIcon } from "../../utils/servapp-icon";
 import { ServAppIcon } from "../../utils/servapp-icon";
 import Chart from 'react-apexcharts';
 import Chart from 'react-apexcharts';
+import { useClientInfos } from "../../utils/hooks";
 
 
 
 
 export const HomeBackground = () => {
 export const HomeBackground = () => {
@@ -87,6 +88,8 @@ const HomePage = () => {
     const theme = useTheme();
     const theme = useTheme();
     const isDark = theme.palette.mode === 'dark';
     const isDark = theme.palette.mode === 'dark';
     const isMd = useMediaQuery(theme.breakpoints.up('md'));
     const isMd = useMediaQuery(theme.breakpoints.up('md'));
+    const {role} = useClientInfos();
+    const isAdmin = role === 1;
 
 
     const blockStyle = {
     const blockStyle = {
         margin: 0,
         margin: 0,
@@ -112,9 +115,13 @@ const HomePage = () => {
     }
     }
 
 
     const refreshConfig = () => {
     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) => {
         API.config.get().then((res) => {
             setConfig(res.data);
             setConfig(res.data);
         });
         });
@@ -213,20 +220,20 @@ const HomePage = () => {
         <HomeBackground status={coStatus} />
         <HomeBackground status={coStatus} />
         <TransparentHeader />
         <TransparentHeader />
         <Stack style={{ zIndex: 2 }} spacing={1}>
         <Stack style={{ zIndex: 2 }} spacing={1}>
-            {coStatus && !coStatus.database && (
+            {isAdmin && coStatus && !coStatus.database && (
                 <Alert severity="error">
                 <Alert severity="error">
                     No Database is setup for Cosmos! User Management and Authentication will not work.<br />
                     No Database is setup for Cosmos! User Management and Authentication will not work.<br />
                     You can either setup the database, or disable user management in the configuration panel.<br />
                     You can either setup the database, or disable user management in the configuration panel.<br />
                 </Alert>
                 </Alert>
             )}
             )}
 
 
-            {coStatus && coStatus.letsencrypt && (
+            {isAdmin && coStatus && coStatus.letsencrypt && (
                 <Alert severity="error">
                 <Alert severity="error">
                     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.
                     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.
                 </Alert>
                 </Alert>
             )}
             )}
 
 
-            {coStatus && coStatus.LetsEncryptErrors && coStatus.LetsEncryptErrors.length > 0 && (
+            {isAdmin && coStatus && coStatus.LetsEncryptErrors && coStatus.LetsEncryptErrors.length > 0 && (
                 <Alert severity="error">
                 <Alert severity="error">
                     There are errors with your Let's Encrypt configuration or one of your routes, please fix them as soon as possible:
                     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) => {
                     {coStatus.LetsEncryptErrors.map((err) => {
@@ -235,25 +242,25 @@ const HomePage = () => {
                 </Alert>
                 </Alert>
             )}
             )}
 
 
-            {coStatus && coStatus.newVersionAvailable && (
+            {isAdmin && coStatus && coStatus.newVersionAvailable && (
                 <Alert severity="warning">
                 <Alert severity="warning">
                     A new version of Cosmos is available! Please update to the latest version to get the latest features and bug fixes.
                     A new version of Cosmos is available! Please update to the latest version to get the latest features and bug fixes.
                 </Alert>
                 </Alert>
             )}
             )}
 
 
-            {coStatus && coStatus.needsRestart && (
+            {isAdmin && coStatus && coStatus.needsRestart && (
                 <Alert severity="warning">
                 <Alert severity="warning">
                     You have made changes to the configuration that require a restart to take effect. Please restart Cosmos to apply the changes.
                     You have made changes to the configuration that require a restart to take effect. Please restart Cosmos to apply the changes.
                 </Alert>
                 </Alert>
             )}
             )}
 
 
-            {coStatus && coStatus.domain && (
+            {isAdmin && coStatus && coStatus.domain && (
                 <Alert severity="error">
                 <Alert severity="error">
                     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.
                     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.
                 </Alert>
                 </Alert>
             )}
             )}
 
 
-            {coStatus && !coStatus.docker && (
+            {isAdmin && coStatus && !coStatus.docker && (
                 <Alert severity="error">
                 <Alert severity="error">
                     Docker is not connected! Please check your docker connection.<br />
                     Docker is not connected! Please check your docker connection.<br />
                     Did you forget to add <pre>-v /var/run/docker.sock:/var/run/docker.sock</pre> to your docker run command?<br />
                     Did you forget to add <pre>-v /var/run/docker.sock:/var/run/docker.sock</pre> to your docker run command?<br />

+ 12 - 9
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 DockerComposeImport from '../servapps/containers/docker-compose';
 import { AppstoreAddOutlined, SearchOutlined } from "@ant-design/icons";
 import { AppstoreAddOutlined, SearchOutlined } from "@ant-design/icons";
 import ResponsiveButton from "../../components/responseiveButton";
 import ResponsiveButton from "../../components/responseiveButton";
+import { useClientInfos } from "../../utils/hooks";
 
 
 function Screenshots({ screenshots }) {
 function Screenshots({ screenshots }) {
   return screenshots.length > 1 ? (
   return screenshots.length > 1 ? (
@@ -23,17 +24,17 @@ function Screenshots({ screenshots }) {
     : <img src={screenshots[0]} style={{ maxHeight: '300px', height: '100%', maxWidth: '100%' }} />
     : <img src={screenshots[0]} style={{ maxHeight: '300px', height: '100%', maxWidth: '100%' }} />
 }
 }
 
 
-function Showcases({ showcase, isDark }) {
+function Showcases({ showcase, isDark, isAdmin }) {
   return (
   return (
     <Carousel animation="slide" navButtonsAlwaysVisible={false} fullHeightHover="true" swipe={false}>
     <Carousel animation="slide" navButtonsAlwaysVisible={false} fullHeightHover="true" swipe={false}>
       {
       {
-        showcase.map((item, i) => <ShowcasesItem isDark={isDark} key={i} item={item} />)
+        showcase.map((item, i) => <ShowcasesItem isDark={isDark} key={i} item={item} isAdmin={isAdmin} />)
       }
       }
     </Carousel>
     </Carousel>
   )
   )
 }
 }
 
 
-function ShowcasesItem({ isDark, item }) {
+function ShowcasesItem({ isDark, item, isAdmin }) {
   return (
   return (
     <Paper style={{
     <Paper style={{
       position: 'relative',
       position: 'relative',
@@ -68,9 +69,9 @@ function ShowcasesItem({ isDark, item }) {
             overflow: 'hidden',
             overflow: 'hidden',
           }}></p>
           }}></p>
           <Stack direction="row" spacing={2} justifyContent="flex-start">
           <Stack direction="row" spacing={2} justifyContent="flex-start">
-            <div>
+            {isAdmin && <div>
               <DockerComposeImport installerInit defaultName={item.name} dockerComposeInit={item.compose} />
               <DockerComposeImport installerInit defaultName={item.name} dockerComposeInit={item.compose} />
-            </div>
+            </div>}
             <Link to={"/cosmos-ui/market-listing/cosmos-cloud/" + item.name} style={{
             <Link to={"/cosmos-ui/market-listing/cosmos-cloud/" + item.name} style={{
               textDecoration: 'none',
               textDecoration: 'none',
             }}>
             }}>
@@ -110,6 +111,8 @@ const MarketPage = () => {
   const isDark = theme.palette.mode === 'dark';
   const isDark = theme.palette.mode === 'dark';
   const { appName, appStore } = useParams();
   const { appName, appStore } = useParams();
   const [search, setSearch] = useState("");
   const [search, setSearch] = useState("");
+  const {role} = useClientInfos();
+  const isAdmin = role === 1;
 
 
   const backgroundStyle = isDark ? {
   const backgroundStyle = isDark ? {
     backgroundColor: 'rgb(0,0,0)',
     backgroundColor: 'rgb(0,0,0)',
@@ -178,7 +181,7 @@ const MarketPage = () => {
           </Link>
           </Link>
 
 
           <div style={{ textAlign: 'center' }}>
           <div style={{ textAlign: 'center' }}>
-            <Screenshots screenshots={openedApp.screenshots} />
+            <Screenshots screenshots={openedApp.screenshots} isAdmin={isAdmin}/>
           </div>
           </div>
 
 
           <Stack direction="row" spacing={2}>
           <Stack direction="row" spacing={2}>
@@ -202,9 +205,9 @@ const MarketPage = () => {
 
 
           <div dangerouslySetInnerHTML={{ __html: openedApp.longDescription }}></div>
           <div dangerouslySetInnerHTML={{ __html: openedApp.longDescription }}></div>
 
 
-          <div>
+          {isAdmin && <div>
             <DockerComposeImport installerInit defaultName={openedApp.name} dockerComposeInit={openedApp.compose} />
             <DockerComposeImport installerInit defaultName={openedApp.name} dockerComposeInit={openedApp.compose} />
-          </div>
+          </div>}
         </Stack>
         </Stack>
       </Stack>
       </Stack>
     </Box>}
     </Box>}
@@ -223,7 +226,7 @@ const MarketPage = () => {
             size={100}
             size={100}
           />
           />
         </Box>}
         </Box>}
-        {showcase && showcase.length > 0 && <Showcases showcase={showcase} isDark={isDark} />}
+        {showcase && showcase.length > 0 && <Showcases showcase={showcase} isDark={isDark} isAdmin={isAdmin} />}
       </Stack>
       </Stack>
 
 
       <Stack spacing={1} style={{
       <Stack spacing={1} style={{

+ 26 - 0
client/src/utils/hooks.js

@@ -0,0 +1,26 @@
+import React from 'react';
+import { useCookies } from 'react-cookie';
+import { logout } from '../api/authentication';
+
+function useClientInfos() {
+  const [cookies] = useCookies(['client-infos']);
+  
+  let clientInfos = null;
+  
+  try {
+    // Try to parse the cookie into a JavaScript object
+    clientInfos = cookies['client-infos'].split(',');
+  } catch (error) {
+    console.error('Error parsing client-infos cookie:', error);
+    logout();
+  }
+  
+  return {
+    nickname: clientInfos[0],
+    role: clientInfos[1]
+  };
+}
+
+export {
+  useClientInfos
+};

+ 30 - 2
package-lock.json

@@ -1,12 +1,12 @@
 {
 {
   "name": "cosmos-server",
   "name": "cosmos-server",
-  "version": "0.8.3",
+  "version": "0.10.0-unstable16",
   "lockfileVersion": 3,
   "lockfileVersion": 3,
   "requires": true,
   "requires": true,
   "packages": {
   "packages": {
     "": {
     "": {
       "name": "cosmos-server",
       "name": "cosmos-server",
-      "version": "0.8.3",
+      "version": "0.10.0-unstable16",
       "dependencies": {
       "dependencies": {
         "@ant-design/colors": "^6.0.0",
         "@ant-design/colors": "^6.0.0",
         "@ant-design/icons": "^4.7.0",
         "@ant-design/icons": "^4.7.0",
@@ -36,6 +36,7 @@
         "react": "^18.2.0",
         "react": "^18.2.0",
         "react-apexcharts": "^1.4.0",
         "react-apexcharts": "^1.4.0",
         "react-color": "^2.19.3",
         "react-color": "^2.19.3",
+        "react-cookie": "^6.1.1",
         "react-copy-to-clipboard": "^5.1.0",
         "react-copy-to-clipboard": "^5.1.0",
         "react-device-detect": "^2.2.2",
         "react-device-detect": "^2.2.2",
         "react-dom": "^18.2.0",
         "react-dom": "^18.2.0",
@@ -3708,6 +3709,11 @@
       "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz",
       "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.1.tgz",
       "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q=="
       "integrity": "sha512-XTIieEY+gvJ39ChLcB4If5zHtPxt3Syj5rgZR+e1ctpmK8NjPf0zFqsz4JpLJT0xla9GFDKjy8Cpu331nrmE1Q=="
     },
     },
+    "node_modules/@types/cookie": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.5.2.tgz",
+      "integrity": "sha512-DBpRoJGKJZn7RY92dPrgoMew8xCWc2P71beqsjyhEI/Ds9mOyVmBwtekyfhpwFIVt1WrxTonFifiOZ62V8CnNA=="
+    },
     "node_modules/@types/hast": {
     "node_modules/@types/hast": {
       "version": "2.3.4",
       "version": "2.3.4",
       "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
       "resolved": "https://registry.npmjs.org/@types/hast/-/hast-2.3.4.tgz",
@@ -8900,6 +8906,19 @@
         "react": "*"
         "react": "*"
       }
       }
     },
     },
+    "node_modules/react-cookie": {
+      "version": "6.1.1",
+      "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-6.1.1.tgz",
+      "integrity": "sha512-fuFRpf8LH6SfmVMowDUIRywJF5jAUDUWrm0EI5VdXfTl5bPcJ7B0zWbuYpT0Tvikx7Gs18MlvAT+P+744dUz2g==",
+      "dependencies": {
+        "@types/hoist-non-react-statics": "^3.3.1",
+        "hoist-non-react-statics": "^3.3.2",
+        "universal-cookie": "^6.0.0"
+      },
+      "peerDependencies": {
+        "react": ">= 16.3.0"
+      }
+    },
     "node_modules/react-copy-to-clipboard": {
     "node_modules/react-copy-to-clipboard": {
       "version": "5.1.0",
       "version": "5.1.0",
       "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz",
       "resolved": "https://registry.npmjs.org/react-copy-to-clipboard/-/react-copy-to-clipboard-5.1.0.tgz",
@@ -10368,6 +10387,15 @@
         "node": ">=4"
         "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": {
     "node_modules/unpipe": {
       "version": "1.0.0",
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",
       "resolved": "https://registry.npmjs.org/unpipe/-/unpipe-1.0.0.tgz",

+ 2 - 1
package.json

@@ -1,6 +1,6 @@
 {
 {
   "name": "cosmos-server",
   "name": "cosmos-server",
-  "version": "0.10.0-unstable16",
+  "version": "0.10.0-unstable17",
   "description": "",
   "description": "",
   "main": "test-server.js",
   "main": "test-server.js",
   "bugs": {
   "bugs": {
@@ -36,6 +36,7 @@
     "react": "^18.2.0",
     "react": "^18.2.0",
     "react-apexcharts": "^1.4.0",
     "react-apexcharts": "^1.4.0",
     "react-color": "^2.19.3",
     "react-color": "^2.19.3",
+    "react-cookie": "^6.1.1",
     "react-copy-to-clipboard": "^5.1.0",
     "react-copy-to-clipboard": "^5.1.0",
     "react-device-detect": "^2.2.2",
     "react-device-detect": "^2.2.2",
     "react-dom": "^18.2.0",
     "react-dom": "^18.2.0",

+ 1 - 1
src/constellation/DNS.go

@@ -176,7 +176,7 @@ func InitDNS() {
 		utils.Log("Loaded " + strconv.Itoa(len(DNSBlacklist)) + " domains")
 		utils.Log("Loaded " + strconv.Itoa(len(DNSBlacklist)) + " domains")
 	}
 	}
 
 
-	if(config.ConstellationConfig.DNS) {
+	if(!config.ConstellationConfig.DNSDisabled) {
 		go (func() {
 		go (func() {
 			dns.HandleFunc(".", handleDNSRequest)
 			dns.HandleFunc(".", handleDNSRequest)
 			server := &dns.Server{Addr: ":" + DNSPort, Net: "udp"}
 			server := &dns.Server{Addr: ":" + DNSPort, Net: "udp"}

+ 2 - 0
src/constellation/api_devices_block.go

@@ -39,6 +39,8 @@ func DeviceBlock(w http.ResponseWriter, req *http.Request) {
 			return
 			return
 		}
 		}
 
 
+		utils.Log("ConstellationDeviceBlocking: Blocking Device " + deviceName)
+
 		c, errCo := utils.GetCollection(utils.GetRootAppId(), "devices")
 		c, errCo := utils.GetCollection(utils.GetRootAppId(), "devices")
 		if errCo != nil {
 		if errCo != nil {
 				utils.Error("Database Connect", errCo)
 				utils.Error("Database Connect", errCo)

+ 41 - 0
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
+// 	}
+// }

+ 7 - 1
src/constellation/api_devices_create.go

@@ -44,11 +44,14 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) {
 		
 		
 		nickname := utils.Sanitize(request.Nickname)
 		nickname := utils.Sanitize(request.Nickname)
 		deviceName := utils.Sanitize(request.DeviceName)
 		deviceName := utils.Sanitize(request.DeviceName)
+		APIKey := utils.GenerateRandomString(32)
 		
 		
 		if utils.AdminOrItselfOnly(w, req, nickname) != nil {
 		if utils.AdminOrItselfOnly(w, req, nickname) != nil {
 			return
 			return
 		}
 		}
 
 
+		utils.Log("ConstellationDeviceCreation: Creating Device " + deviceName)
+
 		c, errCo := utils.GetCollection(utils.GetRootAppId(), "devices")
 		c, errCo := utils.GetCollection(utils.GetRootAppId(), "devices")
 		if errCo != nil {
 		if errCo != nil {
 				utils.Error("Database Connect", errCo)
 				utils.Error("Database Connect", errCo)
@@ -100,6 +103,8 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) {
 				"PublicHostname": request.PublicHostname,
 				"PublicHostname": request.PublicHostname,
 				"Port": request.Port,
 				"Port": request.Port,
 				"Fingerprint": fingerprint,
 				"Fingerprint": fingerprint,
+				"APIKey": APIKey,
+				"Blocked": false,
 			})
 			})
 
 
 			if err3 != nil {
 			if err3 != nil {
@@ -123,7 +128,7 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) {
 			}
 			}
 			
 			
 			// read configYml from config/nebula.yml
 			// 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,
 				Nickname: nickname,
 				DeviceName: deviceName,
 				DeviceName: deviceName,
 				PublicKey: key,
 				PublicKey: key,
@@ -132,6 +137,7 @@ func DeviceCreate(w http.ResponseWriter, req *http.Request) {
 				IsRelay: request.IsRelay,
 				IsRelay: request.IsRelay,
 				PublicHostname: request.PublicHostname,
 				PublicHostname: request.PublicHostname,
 				Port: request.Port,
 				Port: request.Port,
+				APIKey: APIKey,
 			})
 			})
 
 
 			if err != nil {
 			if err != nil {

+ 1 - 1
src/constellation/api_nebula_connect.go

@@ -26,7 +26,7 @@ func API_ConnectToExisting(w http.ResponseWriter, req *http.Request) {
 		config := utils.ReadConfigFromFile()
 		config := utils.ReadConfigFromFile()
 		config.ConstellationConfig.Enabled = true
 		config.ConstellationConfig.Enabled = true
 		config.ConstellationConfig.SlaveMode = true
 		config.ConstellationConfig.SlaveMode = true
-		config.ConstellationConfig.DNS = false
+		config.ConstellationConfig.DNSDisabled = false
 		// ConstellationHostname = 
 		// ConstellationHostname = 
 
 
 		// output utils.CONFIGFOLDER + "nebula.yml"
 		// output utils.CONFIGFOLDER + "nebula.yml"

+ 6 - 4
src/constellation/nebula.go

@@ -235,7 +235,7 @@ func ExportConfigToYAML(overwriteConfig utils.ConstellationConfig, outputPath st
 	return nil
 	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)
 	utils.Log("Exporting YAML config for " + name + " with file " + configPath)
 
 
 	// Read the YAML config file
 	// 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")
 		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
 	// export configMap as YML
 	yamlData, err = yaml.Marshal(configMap)
 	yamlData, err = yaml.Marshal(configMap)

+ 1 - 2
src/market/index.go

@@ -12,7 +12,7 @@ type marketGetResult struct {
 }
 }
 
 
 func MarketGet(w http.ResponseWriter, req *http.Request) {
 func MarketGet(w http.ResponseWriter, req *http.Request) {
-	if utils.AdminOnly(w, req) != nil {
+	if utils.LoggedInOnly(w, req) != nil {
 		return
 		return
 	}
 	}
 
 
@@ -22,7 +22,6 @@ func MarketGet(w http.ResponseWriter, req *http.Request) {
 			return
 			return
 		}
 		}
 		
 		
-		// return the first 10 results of each market
 		marketGetResult := marketGetResult{
 		marketGetResult := marketGetResult{
 			All: make(map[string]interface{}),
 			All: make(map[string]interface{}),
 			Showcase: []appDefinition{},
 			Showcase: []appDefinition{},

+ 25 - 0
src/user/token.go

@@ -5,6 +5,7 @@ import (
 	"github.com/azukaar/cosmos-server/src/utils"
 	"github.com/azukaar/cosmos-server/src/utils"
 	"github.com/golang-jwt/jwt"
 	"github.com/golang-jwt/jwt"
 	"errors"
 	"errors"
+	"strconv"
 	"strings"
 	"strings"
 	"time"
 	"time"
 	"encoding/json"
 	"encoding/json"
@@ -189,13 +190,25 @@ func logOutUser(w http.ResponseWriter, req *http.Request) {
 		HttpOnly: true,
 		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" {
 	if reqHostNoPort == "localhost" || reqHostNoPort == "0.0.0.0" {
 		cookie.Domain = ""
 		cookie.Domain = ""
+		clientCookie.Domain = ""
 	} else {
 	} else {
 		cookie.Domain = "." + reqHostNoPort
 		cookie.Domain = "." + reqHostNoPort
+		clientCookie.Domain = "." + reqHostNoPort
 	}
 	}
 
 
 	http.SetCookie(w, &cookie)
 	http.SetCookie(w, &cookie)
+	http.SetCookie(w, &clientCookie)
 
 
 	// TODO: logout every other device if asked by increasing passwordcycle
 	// 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,
 		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)
 	utils.Log("UserLogin: Setting cookie for " + reqHostNoPort)
 
 
 	if reqHostNoPort == "localhost" || reqHostNoPort == "0.0.0.0" {
 	if reqHostNoPort == "localhost" || reqHostNoPort == "0.0.0.0" {
 		cookie.Domain = ""
 		cookie.Domain = ""
+		clientCookie.Domain = ""
 	} else {
 	} else {
 		if utils.IsValidHostname(reqHostNoPort) {
 		if utils.IsValidHostname(reqHostNoPort) {
 			cookie.Domain = "." + reqHostNoPort
 			cookie.Domain = "." + reqHostNoPort
+			clientCookie.Domain = "." + reqHostNoPort
 		} else {
 		} else {
 			utils.Error("UserLogin: Invalid hostname", nil)
 			utils.Error("UserLogin: Invalid hostname", nil)
 			utils.HTTPError(w, "User Logging Error", http.StatusInternalServerError, "UL001")
 			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, &cookie)
+	http.SetCookie(w, &clientCookie)
 }
 }

+ 2 - 1
src/utils/types.go

@@ -213,7 +213,7 @@ type ConstellationConfig struct {
 	Enabled bool
 	Enabled bool
 	SlaveMode bool
 	SlaveMode bool
 	PrivateNode bool
 	PrivateNode bool
-	DNS bool
+	DNSDisabled bool
 	DNSPort string
 	DNSPort string
 	DNSFallback string
 	DNSFallback string
 	DNSBlockBlacklist bool
 	DNSBlockBlacklist bool
@@ -239,6 +239,7 @@ type ConstellationDevice struct {
 	Port string `json:"port"`
 	Port string `json:"port"`
 	Blocked bool `json:"blocked"`
 	Blocked bool `json:"blocked"`
 	Fingerprint string `json:"fingerprint"`
 	Fingerprint string `json:"fingerprint"`
+	APIKey string `json:"-"`
 }
 }
 
 
 type NebulaFirewallRule struct {
 type NebulaFirewallRule struct {

+ 1 - 1
src/utils/utils.go

@@ -64,7 +64,7 @@ var DefaultConfig = Config{
 	},
 	},
   ConstellationConfig: ConstellationConfig{
   ConstellationConfig: ConstellationConfig{
     Enabled: false,
     Enabled: false,
-		DNS: true,
+		DNSDisabled: false,
 		DNSFallback: "8.8.8.8:53",
 		DNSFallback: "8.8.8.8:53",
 		DNSAdditionalBlocklists: []string{
 		DNSAdditionalBlocklists: []string{
 			"https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt",
 			"https://s3.amazonaws.com/lists.disconnect.me/simple_tracking.txt",