====== OpenFGA DSL Syntax: Keywords vs Names ====== In the DSL there are two kinds of words: **keywords** you must type exactly, and **names** you make up (or point to). Symbols like ''​:​'' and ''[ ]'' are also fixed. ===== Keywords (fixed — you cannot change them) ===== Built-in words. Spell them exactly. ^ Keyword ^ What it does ^ | ''model'' | Starts the file. | | ''schema'' | Comes with the version, like ''schema 1.1''. | | ''type'' | Starts a new type. | | ''relations'' | Starts the list of relations for a type. | | ''define'' | Starts one relation. | | ''or'', ''and'', ''but not'' | Combine relations. | | ''from'' | Inherit through a link. | ===== Names (values — you choose these) ===== Words //you// invent to fit your app. * **Type names**: ''user'', ''document'', ''folder'', ''organization''. You could call them ''person'' or ''file'' instead. * **Relation names**: ''owner'', ''editor'', ''viewer'', ''parent'', ''member''. You pick these too. There is a small twist. Sometimes you //create// a name, and sometimes you //refer// to a name you already made. * ''define viewer: ...'' → here you **create** the name ''viewer''. * ''... or owner'' → here you **refer** to ''owner'', which must already exist. * ''[user]'' → ''user'' must be a type you already defined. ===== Symbols (fixed punctuation) ===== * ''​:​'' after a relation name — required. * ''[ ]'' around allowed types — required for a direct relation. * ''#'' — used in tuples, like ''group:eng#member'' (not in the model itself). ===== One line, broken into parts ===== Take this line: define viewer: [user] or owner ^ Token ^ Kind ^ Note ^ | ''define'' | keyword | fixed word | | ''viewer'' | name | you **create** it here | | ''​:​'' | symbol | required | | ''['' | symbol | required | | ''user'' | name | a type that must already exist | | '']'' | symbol | required | | ''or'' | keyword | fixed word | | ''owner'' | name | a relation that must already exist | ===== The general shape ===== Uppercase = keywords (fixed). ''<...>'' = names you supply. MODEL SCHEMA 1.1 TYPE RELATIONS DEFINE : [] OR ===== The keywords as concepts ===== Each keyword plays one clear role. Think of building the model top to bottom: first the file, then a type, then its relations, then each rule. ==== model ==== **Concept:** the start of the whole file. It wraps everything. You write it once, at the very top. Nothing lives outside it. model schema 1.1 ==== schema ==== **Concept:** the DSL version number. It tells OpenFGA which grammar to use. Always pair it with a version, like ''1.1''. Put it right under ''model''. schema 1.1 ==== type ==== **Concept:** a //kind of thing// in your system. A type is a category of object, like a user, a document, or a folder. You will have one ''type'' block for each kind of thing. The name after ''type'' is yours to choose. type document Read it as: **"documents are a kind of thing in this system."** ==== relations ==== **Concept:** the heading that opens the list of links for a type. It has no rule of its own. It just says "the rules for this type start here." Everything indented under it belongs to that type. type document relations define owner: [user] define viewer: [user] or owner Read it as: **"here come the relations that documents can have."** ==== define ==== **Concept:** one single rule (one relation). Each ''define'' line creates one named link and says who can have it. You will have many ''define'' lines under ''relations''. define owner: [user] Read it as: **"define a link called //owner//; a user can be assigned to it."** ==== or / and / but not ==== **Concept:** ways to combine relations into one rule. * ''or'' — this **or** that (widest access). * ''and'' — this **and** that (both required). * ''but not'' — this **except** that (takes access away). define editor: [user] or owner define can_delete: owner and admin define viewer: [user] but not blocked ==== from ==== **Concept:** inherit access through a link to another object. It lets access flow between objects. You name a link to follow, then a relation to check on the other side. define viewer: viewer from parent Read it as: **"you are a viewer here if you are a viewer of the //parent//."** ===== How they nest ===== The keywords stack inside each other, shown by indentation: model ← the whole file schema 1.1 ← the DSL version type document ← a kind of thing relations ← its links start here define owner: [user] ← one rule define editor: [user] or owner ← one rule, using "or" Reading order: ''model'' → ''schema'' → ''type'' → ''relations'' → ''define''. Outer keywords set the container; inner ones fill in the detail. ===== More examples to read ===== The trick to reading a relation is to say it out loud in plain words. Below, the left side is the DSL, the right side is how you say it. ==== Direct grant ==== define owner: [user] Read it as: **"a user can be assigned as the owner."** \\ Keyword: ''define''. Names: ''owner'', ''user''. This one needs a tuple to be true. ==== Union with ''or'' ==== define editor: [user] or owner Read it as: **"an editor is a user assigned directly, //or// anyone who is already an owner."** \\ So every owner is also an editor, for free. ==== More than one allowed type ==== define member: [user, group#member] Read it as: **"a member can be a single user, //or// every member of a group."** \\ The comma lists two allowed types. ''group#member'' means "the members of a group." ==== Public access with a wildcard ==== define viewer: [user:*] Read it as: **"every user can view this."** \\ The ''*'' is a wildcard. Use it for public things. ==== Both must be true with ''and'' ==== define can_delete: owner and admin Read it as: **"you can delete only if you are //both// an owner //and// an admin."** \\ ''and'' means both conditions must hold at the same time. ==== Take away with ''but not'' ==== define viewer: [user] but not blocked Read it as: **"a viewer is any assigned user, //except// anyone who is blocked."** \\ ''but not'' removes people, even if they would otherwise qualify. ==== Inherit through a link with ''from'' ==== define viewer: viewer from parent Read it as: **"you can view this if you are a viewer of its parent."** \\ Follow the ''parent'' link, then check ''viewer'' on whatever you land on. ==== Everything combined ==== define viewer: [user] or editor or viewer from parent Read it as: **"a viewer is a user assigned directly, //or// an editor, //or// a viewer of the parent folder."** \\ This is the common real-world pattern: direct people, higher roles, and inherited folder access all at once. ===== Quick reading table ===== ^ DSL ^ Read it as ^ | ''[user]'' | assigned directly, needs a tuple | | ''[user, team#member]'' | a user, or every member of a team | | ''[user:*]'' | everyone (public) | | ''or owner'' | also counts if you are an owner | | ''and admin'' | only if you are also an admin | | ''but not blocked'' | unless you are blocked | | ''viewer from parent'' | if you are a viewer of the parent | Rule of thumb: ''[ ]'' means a //direct// grant (a tuple sets it). A bare relation name means a //computed// grant (worked out from other relations, no tuple needed). ===== See also ===== * [[security:authorization:openfga:authorization-model|Authorization model]] * [[security:authorization:openfga:relationship-tuples|Relationship tuples]]