I'm struggling to find a way to make a model that can work for multiple organizations within the platform I want to build with AWS Amplify.
The use case is a sort of corporate intranet, where a user registers on the platform and has access to a Workspace. Within the Workspace, posts are created, and posts are commented on.
This is the example model:
type Workspace
#model(subscriptions: null)
#auth(
rules: [
{ allow: owner, operations: [create, read], ownerField: "owner" }
{ allow: private, provider: iam, operations: [read, delete] }
]
) {
id: ID!
title: String!
planCode: PlanType!
plan: Plan! #connection(fields: ["planCode"])
posts: [Post]! #connection(name: "workspacePosts", sortField: "createdAt")
totalPosts: Int!
owner: String!
}
type Post
#model(subscriptions: null)
#auth(
rules: [
{ allow: owner, ownerField: "owner", }
]
) {
id: ID!
title: String
workspace: Workspace! #connection(name: "workspacePosts")
comments: [Comment]! #connection(name: "postComments")
totalComments: Int!
owner: String!
}
type Comment
#model(subscriptions: null)
#auth(
rules: [
{ allow: owner, ownerField: "owner", }
]
) {
id: ID!
content: String
post: Post! #connection(name: "postComments")
owner: String!
}
enum PlanType {
FREE
BASIC
PRO
}
The Posts, Comments and the Workspace are private and can only be viewed by the Workspace owner, who will also be the owner of the Posts and comments.
At some point, the user must be able to invite another user into his Workspace, and the new user must then be able to access all the records that the previous owner has generated (Workspace, Posts and Comments).
In this case, with the addition of the new user I am forced to:
get all the Posts related to the Workspace
get all the Comments related to the Posts related to the Workspace
update every record from owner: "User_1" to owner: ["User_1", "User_2"]
Everything related to the Workspace must be visible or "owned" by every new users added to the Workspace.
Is there a more efficient way?
For example, is it possible to add an owner to the Workspace and automatically give access to all Posts and Comments related to the workspace, if the user is the owner of the Workspace?
Are there any other options?
Suggestion for a more efficient way is to use ControlTower and any rule there would apply, especially if you want to do it through SSO.
Related
I implemented a post view system with MongoDB which adds a new record to the database every time a user sees a post.
On the front-end this works via the Intersection Observer API. When it's triggered, it makes a POST request to the API.
const postStatSchema = new Schema({
type: { type: String, required: true },
post_id: { type: mongoose.Types.ObjectId, ref: "Post", required: true },
from_user_id: { type: mongoose.Types.ObjectId, ref: "User" },
ip_address: { type: String },
date: { type: Date },
});
type in this case is "view".
The scenarios:
User sees the post.
API call.
I check if an existing PostStat document exists with that post_id, ip_address OR from_user_id. If it exists, I stop the execution of the call and return.
If it doesn't exist, I create a new PostStat document and save it.
I've been having about 400 users a day in the last few days and I noticed how quickly my DB storage grew through this strategy. I was wondering if this is actually a terrible strategy and not easy to scale.
Any ideas on how to improve this process? The reason I need the date is that I need the ability to sort the posts based on a timeframe.
Developing a discord moderation bot with Discord.js
Facing error "DiscordAPIError: Missing Access"
My bot user, which has all server permissions except "Administrator", and highest.rawPosition is 18 (highest of all roles) :
user: ClientUser {
id: 'XXXXXXXXX',
bot: true,
username: 'Bot',
discriminator: '1863',
avatar: '2e8af5cccdc5cf15a0f88818dbb044e6',
lastMessageID: null,
lastMessageChannelID: null,
verified: true,
mfaEnabled: true,
_typing: Map {}
},
is trying to add this role (rawPosition is 2) :
Role {
id: 'XXXXXXXXX',
name: 'Mod',
color: 0,
hoist: false,
rawPosition: 2,
permissions: Permissions { bitfield: 37211712 },
managed: false,
mentionable: false,
deleted: false
}
as an overwrite permission to this channel :
CategoryChannel {
type: 'category',
deleted: false,
id: 'XXXXXXXXX',
name: 'Section Job',
rawPosition: 6,
parentID: null,
}
using this code :
await channel.createOverwrite(role, {
VIEW_CHANNEL: true,
READ_MESSAGES: true,
SEND_MESSAGES: true,
CONNECT: true
});
Here is the error I get :
[2020/10/06 00:52:56:427] DiscordAPIError: Missing Access
I performed a search before posting this.
Discord gives many possible explanations about this specific error, and none would fit with my issue : https://discordjs.guide/popular-topics/permissions-extended.html#missing-permissions
Your bot is missing the needed permission to execute this action in it's calculated base or final permissions (requirement changes based on the type of action you are trying to execute).
--> My bot has all permissions except "Administrator"
You provided an invalid permission number while trying to create overwrites. (The calculator on the apps page returns decimal values while the developer documentation lists the flags in hex. Make sure you are not mixing the two and don't use the hex prefix 0x where not applicable)
--> I give the exact same permissions, formatted exactly the same as another group, and it works
It is trying to execute an action on a guild member with a role higher than or equal to your bots highest role.
--> Not executing an action on a user, but on a group
It is trying to modify or assign a role that is higher than or equal to its highest role.
--> Bot highest role is 18, and added role rawPosition is 2
It is trying to add a managed role to a member.
--> As you can see it is not "managed"
It is trying to remove a managed role from a member.
--> Not trying to remove a role
It is trying to execute a forbidden action on the server owner.
--> Not editing a user, but a channel (and server owwner does not have this role)
It is trying to execute an action based on another unfulfilled factor (for example reserved for partnered guilds).
--> I don't understand this one but it couldn't be that
It is trying to execute an action on a voice channel without the VIEW_CHANNEL permission.
--> Not executing an action on a voice channel, but on a Category channel, and it has VIEW_CHANNEL permission
More information :
1/ The same command in the exact same context works with some other groups, like this one :
Role {
id: 'XXXXXXXXX',
name: 'Job',
color: 0,
hoist: false,
rawPosition: 1,
permissions: Permissions { bitfield: 37211712 },
managed: false,
mentionable: false,
deleted: false
}
2/ The same command works with "Administrator" permission assigned to the bot
3/ Of course, adding "Administrator" permission to the bot is not an option
Thanks for any help !
Ok then I found the solution thanks to discord Js community
I forgot to give my bot read permissions on that specific channel, so the bot couldn't give permissions to a channel he didn't have access to...
I just added
permissionOverwrites: [
{
id: guild.me.roles.highest,
allow: ['VIEW_CHANNEL', 'MANAGE_CHANNELS', 'MANAGE_ROLES']
}
]
to my channel creation, and it works.
I am creating a bot command so that when you type !setup it sets up the whole server (creating channels and roles, etc). I have already set it to create the roles and channels but there are some channels that only specific roles can type in, whereas, other roles can only read the channel. I don't know how to set up permission overwrites for certain roles.
I have already looked on the Discord.js documentation and it doesn't help much. I receive an error that supplied parameter was neither a user nor a role, but I don't know how to gain the role ID.
message.guild.createRole({
name: 'Admin',
color: '#2494ad',
permissions: ['ADMINISTRATOR', 'VIEW_AUDIT_LOG', 'MANAGE_GUILD', 'MANAGE_CHANNELS', 'SEND_TTS_MESSAGES', 'CREATE_INSTANT_INVITE', 'KICK_MEMBERS', 'BAN_MEMBERS', 'ADD_REACTIONS', 'PRIORITY_SPEAKER', 'READ_MESSAGES', 'SEND_MESSAGES', 'MANAGE_MESSAGES', 'EMBED_LINKS', 'ATTACH_FILES', 'READ_MESSAGE_HISTORY', 'MENTION_EVERYONE', 'USE_EXTERNAL_EMOJIS', 'CONNECT', 'SPEAK', 'MUTE_MEMBERS', 'DEAFEN_MEMBERS', 'MOVE_MEMBERS', 'USE_VAD', 'CHANGE_NICKNAME', 'MANAGE_NICKNAMES', 'MANAGE_ROLES', 'MANAGE_WEBHOOKS', 'MANAGE_EMOJIS']
});
message.guild.createRole({
name: 'Requesting Role',
color: '#1bb738',
permissions: ['READ_MESSAGES', 'SEND_MESSAGES', 'READ_MESSAGE_HISTORY',]
});
//Categories and Channels
message.guild.createChannel('clan-communications', {
type: 'category',
permissionOverwrites: [{
id:'25311096',
deny: ['SEND_MESSAGES']
}]
});
//Permissions
message.channel.overwritePermissions('25311096', {
SEND_MESSAGES: false
});
break;
I would like roles to have base permissions for the whole server. But certain roles have overwrites for different channels. Instead it says the supplied parameter was neither a user nor a role.
First and foremost, welcome to Stack Overflow. Hopefully we can be of help to you.
Let's walk through a solution step by step to achieve your desired result.
When you create your roles, you should declare a variable to store them. That way, you can use what the client just created later on. Since Guild.createRole() returns a promise, we can save the fulfilled result into a variable.
Note that the keyword await must be placed in async context (within an async function).
const vip = await message.guild.createRole('VIP', { ... });
Then, when you need to use that role, you can refer to the variable.
await message.guild.createChannel('VIPs Only', {
type: 'category',
permissionOverwrites: [
{
id: vip.id,
allow: ['READ_MESSAGES']
}, {
id: message.guild.defaultRole.id, // #everyone role
deny: ['READ_MESSAGES']
}
]
});
If the role already exists, you can retrieve it from the Collection of roles returned by Guild.roles.
const testRole = message.guild.roles.get('IDhere');
const adminRole = message.guild.roles.find(r => r.name === 'Admin');
Other simple improvements.
In your code snippet, I don't see any error handling. The methods you're using return promises. If an error occurs in any, the promise state will become rejected. Any rejected promises that aren't caught will throw errors. To fix this, attach catch() methods to each promise (better for individual handling) or wrap async code in a try...catch statement.
When creating your Admin role, know that the ADMINISTRATOR permission automatically grants all other permissions (and allows users to bypass channel-specific permissions). Therefore, there's no need to list any other permission, or change the role's permission overwrites in any channels.
Your IDs in your code don't appear to be valid. Discord IDs are snowflakes, represented by strings of 18 digits. You can access them through Developer Mode in Discord. However, you shouldn't have to hard-code them with this solution, unless they already exist.
In the permissions array for your Requesting Role role, you have an extra comma.
In Firebase Database; If user_a has data they can access and they want to share this data with user_b, what is the best practice and database structure for securely sharing this data between specific users?
Important: user_a doesn't have any information about user_b account, e.g. uid.
Detailed Example:
1) user_a has a list of clientele.
"users": {
"user_a": {
"clients": [
"clientUid",
"clientUid2"
]
}
},
"clients": {
"clientUid": {
"firstName": "John",
"lastName": "Doe"
},
"clientUid2": {
"firstName": "Joe",
"lastName": "Bloggs"
}
}
2) user_b signs up. user_a now wants to share user_b data in clients with the user_b account that signed up.
Put another way: user_a has a list of clients, one of them creates an account and needs to link to the information user_a has already entered for them.
An important element here is that there is no list of users or accounts that a 'friend request' can be made from for more data. I have experimented with creating a short unique id a user can enter when they sign up to access their data, but am unsure if this is a good way forward and have encountered issues.
This is different to previously asked 'shared data' questions as it focuses on, how is the link between the two users made? Not just the before and after.
I know if two ways:
Either the users must both know a certain value, known as a shared secret.
Or you allow the users to search for each other.
Shared secret
A shared secret can be easily modeled in the database as a location that you can read once you know it. Your short unique id is an example of such a shared secret, where user_a "determines" the secret, and then sends it to user_b out of band.
A simple flow:
user_a clicks a Get secret button in the app
The app determines a secret code, e.g. correct-horse-battery-staple and:
2a. Writes the secret code to the database in a non-queryable-but-readable location, together with the actual information to share, in your case the UID of user_a.
2b. Shows the secret code to user_a.
user_a copies the code and sends it to user_b via some other communication mechanism, e.g. text, email, chat.
user_b clicks Enter secret button in the app, and pastes the secret.
The app reads the location, and is able to read the UID of user_a.
The data structure for this could be something like:
"secrets": {
"correct-horse-battery-staple": "uidOfUserA"
}
Which you secure with these rules:
{
"rules": {
"secrets": {
"$secret": {
".read": true
}
}
}
}
So the above rules don't allow anyone to read /secrets, hence they can't get a list of all secrets. But once they know a secret, they can look up the associated data under it.
Allowing search
The alternative is to have a list of users in your database, and allow user_b to search on some property that they know from user_a. A common property might be their email address, or their display name, which you could store in the database like:
users: {
"uidOfUserA": {
email: "userA#domain.com",
displayName: "user A"
},
"uidOfUserB": {
email: "userB#anotherdomain.com",
displayName: "user B"
}
}
To allow users to search each other is a bit more involved, as in: it will require server-side code. The reason for this is that being able to search over a dataset requires that you can read that dataset. And in Firebase, if you can read a dataset, you can get all data under it. In other words, to allow search a user you need to be able to read all of /users and allowing this would disclose too much information.
So to implement a search securely, you'll need to have rules like this:
{
"rules": {
"users": {
"$uid": {
".read": true,
".write": "auth.uid === $uid"
}
}
}
}
In words: anyone can read the profile for a user for whom they know the UID, but each individual user can only modify their own profile.
With this, you can implement a Cloud Function that then takes the email or display name as input, and searches across all /users. Since Cloud Functions access the database with administrative privileges, they are not restricted by the above security rules.
Iam new to graphql.Iam implementing a react-native app using aws appsync.We are facing problem in implementing #isUnique and giving it is giving me an error
Failed to parse schema document - ensure it's a valid SDL-formatted document.
type Person {
id: ID! #isUnique
createdAt: String!
updateAt: String!
name: String!
messages: [Messages!]!
#relation(name: "UserMessages")}
I want to have a random id generated for each user in the schema.How can we achieve this.
Thanks in Advance!!
#isUnique is a provider-specific directive. If you want to generate unique identifiers on the server with AWS AppSync, you can use $util.autoId() in the resolver. The AWS AppSync - Up and Running blog post has details.
First, it's #unique if you're using appolo-server and #isUnique if graphcool. Second, ids will be unique by default there is no need for #unique. It's for other fields like email or username.
Take a look here Schema directives - Appolo
If you're using Graphcool this