diff --git a/server/server_account.go b/server/server_account.go
index 28a6dfde..a3565e54 100644
--- a/server/server_account.go
+++ b/server/server_account.go
@@ -316,10 +316,13 @@ func (s *Server) handleAccountAccessAdd(w http.ResponseWriter, r *http.Request,
if !topicRegex.MatchString(req.Topic) {
return errHTTPBadRequestTopicInvalid
}
+ // FIXME authorize: how do I know if v.user (= auth'd user) is allowed to write the ACL entries
+ everyoneRead := util.Contains([]string{"read-write", "rw", "read-only", "read", "ro"}, req.Everyone)
+ everyoneWrite := util.Contains([]string{"read-write", "rw", "write-only", "write", "wo"}, req.Everyone)
if err := s.userManager.AllowAccess(v.user.Name, req.Topic, true, true); err != nil {
return err
}
- if err := s.userManager.AllowAccess(user.Everyone, req.Topic, false, false); err != nil {
+ if err := s.userManager.AllowAccess(user.Everyone, req.Topic, everyoneRead, everyoneWrite); err != nil {
return err
}
w.Header().Set("Content-Type", "application/json")
diff --git a/server/types.go b/server/types.go
index e274624d..97acfbb7 100644
--- a/server/types.go
+++ b/server/types.go
@@ -268,6 +268,6 @@ type apiAccountResponse struct {
}
type apiAccountAccessRequest struct {
- Topic string `json:"topic"`
- Access string `json:"access"`
+ Topic string `json:"topic"`
+ Everyone string `json:"everyone"`
}
diff --git a/user/manager.go b/user/manager.go
index c4d4de8d..54912f17 100644
--- a/user/manager.go
+++ b/user/manager.go
@@ -47,7 +47,8 @@ const (
);
CREATE UNIQUE INDEX idx_user ON user (user);
CREATE TABLE IF NOT EXISTS user_access (
- user_id INT NOT NULL,
+ user_id INT NOT NULL,
+ owner_user_id INT,
topic TEXT NOT NULL,
read INT NOT NULL,
write INT NOT NULL,
diff --git a/web/src/components/ActionBar.js b/web/src/components/ActionBar.js
index 572f713c..a52a3be7 100644
--- a/web/src/components/ActionBar.js
+++ b/web/src/components/ActionBar.js
@@ -33,6 +33,7 @@ import Divider from "@mui/material/Divider";
import {Logout, Person, Settings} from "@mui/icons-material";
import ListItemIcon from "@mui/material/ListItemIcon";
import accountApi, {UnauthorizedError} from "../app/AccountApi";
+import PopupMenu from "./PopupMenu";
const ActionBar = (props) => {
const { t } = useTranslation();
@@ -189,6 +190,7 @@ const SettingsIcons = (props) => {
{
}
{
);
};
-const PopupMenu = (props) => {
- return (
-
- );
-};
-
export default ActionBar;
diff --git a/web/src/components/PopupMenu.js b/web/src/components/PopupMenu.js
new file mode 100644
index 00000000..6d39d86e
--- /dev/null
+++ b/web/src/components/PopupMenu.js
@@ -0,0 +1,47 @@
+import {Menu} from "@mui/material";
+import * as React from "react";
+
+const PopupMenu = (props) => {
+ const horizontal = props.horizontal ?? "left";
+ const arrow = (horizontal === "right") ? { right: 19 } : { left: 19 };
+ return (
+
+ );
+};
+
+export default PopupMenu;
diff --git a/web/src/components/SubscribeDialog.js b/web/src/components/SubscribeDialog.js
index 8f42869b..f5add414 100644
--- a/web/src/components/SubscribeDialog.js
+++ b/web/src/components/SubscribeDialog.js
@@ -20,6 +20,11 @@ import routes from "./routes";
import accountApi, {UnauthorizedError} from "../app/AccountApi";
import IconButton from "@mui/material/IconButton";
import PublicIcon from '@mui/icons-material/Public';
+import LockIcon from '@mui/icons-material/Lock';
+import PublicOffIcon from '@mui/icons-material/PublicOff';
+import MenuItem from "@mui/material/MenuItem";
+import PopupMenu from "./PopupMenu";
+import ListItemIcon from "@mui/material/ListItemIcon";
const publicBaseUrl = "https://ntfy.sh";
@@ -75,11 +80,15 @@ const SubscribePage = (props) => {
const { t } = useTranslation();
const [anotherServerVisible, setAnotherServerVisible] = useState(false);
const [errorText, setErrorText] = useState("");
+ const [accessAnchorEl, setAccessAnchorEl] = useState(null);
+ const [access, setAccess] = useState("public");
const baseUrl = (anotherServerVisible) ? props.baseUrl : config.baseUrl;
const topic = props.topic;
const existingTopicUrls = props.subscriptions.map(s => topicUrl(s.baseUrl, s.topic));
- const existingBaseUrls = Array.from(new Set([publicBaseUrl, ...props.subscriptions.map(s => s.baseUrl)]))
+ const existingBaseUrls = Array
+ .from(new Set([publicBaseUrl, ...props.subscriptions.map(s => s.baseUrl)]))
.filter(s => s !== config.baseUrl);
+
const handleSubscribe = async () => {
const user = await userManager.get(baseUrl); // May be undefined
const username = (user) ? user.username : t("subscribe_dialog_error_user_anonymous");
@@ -97,10 +106,12 @@ const SubscribePage = (props) => {
console.log(`[SubscribeDialog] Successful login to ${topicUrl(baseUrl, topic)} for user ${username}`);
props.onSuccess();
};
+
const handleUseAnotherChanged = (e) => {
props.setBaseUrl("");
setAnotherServerVisible(e.target.checked);
};
+
const subscribeButtonEnabled = (() => {
if (anotherServerVisible) {
const isExistingTopicUrl = existingTopicUrls.includes(topicUrl(baseUrl, topic));
@@ -110,6 +121,7 @@ const SubscribePage = (props) => {
return validTopic(topic) && !isExistingTopicUrl;
}
})();
+
const updateBaseUrl = (ev, newVal) => {
if (validUrl(newVal)) {
props.setBaseUrl(newVal.replace(/\/$/, '')); // strip trailing slash after https?://
@@ -117,6 +129,7 @@ const SubscribePage = (props) => {
props.setBaseUrl(newVal);
}
};
+
return (
<>
{t("subscribe_dialog_subscribe_title")}
@@ -125,9 +138,13 @@ const SubscribePage = (props) => {
{t("subscribe_dialog_subscribe_description")}
-
-
-
+ {session.exists() &&
+
setAccessAnchorEl(ev.currentTarget)} color="inherit" size="large" edge="start" sx={{height: "45px", marginTop: "5px", color: "grey"}}>
+ {access === "public" && }
+ {access === "public-read" && }
+ {access === "private" && }
+
+ }
{
maxLength: 64,
"aria-label": t("subscribe_dialog_subscribe_topic_placeholder")
}}
- />
-
+ />
+
+ setAccessAnchorEl(null)}
+ >
+
+
+
+