text: (self.webpackChunkdocusaurus_template=self.webpackChunkdocusaurus_template||[]).push([[3980],{58215:function(e,t,a){"use strict";var n=a(67294);t.Z=function(e){var t=e.children,a=e.hidden,s=e.className;return n.createElement("div",{role:"tabpanel",hidden:a,className:s},t)}},55064:function(e,t,a){"use strict";a.d(t,{Z:function(){return c}});var n=a(67294),s=a(79443);var o=function(){var e=(0,n.useContext)(s.Z);if(null==e)throw new Error('"useUserPreferencesContext" is used outside of "Layout" component.');return e},r=a(86010),i="tabItem_1uMI",l="tabItemActive_2DSg";var c=function(e){var t,a=e.lazy,s=e.block,c=e.defaultValue,p=e.values,u=e.groupId,d=e.className,h=n.Children.toArray(e.children),m=null!=p?p:h.map((function(e){return{value:e.props.value,label:e.props.label}})),f=null!=c?c:null==(t=h.find((function(e){return e.props.default})))?void 0:t.props.value,g=o(),b=g.tabGroupChoices,v=g.setTabGroupChoices,w=(0,n.useState)(f),y=w[0],k=w[1],x=[];if(null!=u){var j=b[u];null!=j&&j!==y&&m.some((function(e){return e.value===j}))&&k(j)}var N=function(e){var t=e.currentTarget,a=x.indexOf(t),n=m[a].value;k(n),null!=u&&(v(u,n),setTimeout((function(){var e,a,n,s,o,r,i,c;(e=t.getBoundingClientRect(),a=e.top,n=e.left,s=e.bottom,o=e.right,r=window,i=r.innerHeight,c=r.innerWidth,a>=0&&o<=c&&s<=i&&n>=0)||(t.scrollIntoView({block:"center",behavior:"smooth"}),t.classList.add(l),setTimeout((function(){return t.classList.remove(l)}),2e3))}),150))},E=function(e){var t,a=null;switch(e.key){case"ArrowRight":var n=x.indexOf(e.target)+1;a=x[n]||x[0];break;case"ArrowLeft":var s=x.indexOf(e.target)-1;a=x[s]||x[x.length-1]}null==(t=a)||t.focus()};return n.createElement("div",{className:"tabs-container"},n.createElement("ul",{role:"tablist","aria-orientation":"horizontal",className:(0,r.Z)("tabs",{"tabs--block":s},d)},m.map((function(e){var t=e.value,a=e.label;return n.createElement("li",{role:"tab",tabIndex:y===t?0:-1,"aria-selected":y===t,className:(0,r.Z)("tabs__item",i,{"tabs__item--active":y===t}),key:t,ref:function(e){return x.push(e)},onKeyDown:E,onFocus:N,onClick:N},null!=a?a:t)}))),a?(0,n.cloneElement)(h.filter((function(e){return e.props.value===y}))[0],{className:"margin-vert--md"}):n.createElement("div",{className:"margin-vert--md"},h.map((function(e,t){return(0,n.cloneElement)(e,{key:t,hidden:e.props.value!==y})}))))}},57203:function(e,t,a){"use strict";a.d(t,{Z:function(){return h}});var n=a(67294),s=a(55064),o=a(58215),r=a(83300),i=a.n(r),l=a(11756),c="container_2x1S",p=a(25108),u=function(e,t){if(!e)return 0;var a=t.findIndex((function(t){return t.indexOf(e)>-1}));return-1===a?0:a},d=function(e){var t=e.src,a=e.title,s=(0,n.useState)(""),o=s[0],r=s[1];(0,n.useEffect)((function(){var a,n,s;i()(t.replace("github.com","raw.githubusercontent.com").replace("/blob/","/")).then((function(e){return e.text()})).then((a=e,n=a.startAt,s=a.endAt,function(e){var t=e.split("\n"),a=u(n,t);a>0&&(t=["// ..."].concat(t.slice(a,-1)));var o=u(s,t);return o>0&&(t=[].concat(t.slice(0,o+1),["// ..."])),t.join("\n")})).then(r).catch(p.error)}),[]);var d="language-"+function(e){var t=e.split(".").pop();switch(t){case"jsx":return"jsx";case"tsx":return"tsx";case"ts":return"typescript";case"go":return"go";case"yaml":case"yml":return"yaml";case"js":return"javascript";case"html":return"html";case"pug":return"pug";default:return t}}(t),h='title="'+(a||function(e){var t=e.match(new RegExp("https://github.com/[^/]+/[^/]+/blob/[^/]+/(.+)","i"))||[];return t.length>=2?t[1]:e}(t))+'"';return n.createElement("div",{className:c},n.createElement(l.Z,{metastring:h,className:d,children:o}))},h=function(e){var t=e.sampleId,a=e.version;return n.createElement(n.Fragment,null,n.createElement(s.Z,{values:[{label:"gRPC Go",value:"grpc-go"},{label:"gRPC node.js",value:"grpc-nodejs"},{label:"REST",value:"rest"},{label:"Keto Client CLI",value:"cli"}],defaultValue:"grpc-go"},n.createElement(o.Z,{value:"grpc-go"},n.createElement(d,{src:"https://github.com/ory/keto/blob/"+a+"/contrib/docs-code-samples/"+t+"/main.go"})),n.createElement(o.Z,{value:"grpc-nodejs"},n.createElement(d,{src:"https://github.com/ory/keto/blob/"+a+"/contrib/docs-code-samples/"+t+"/index.js"})),n.createElement(o.Z,{value:"rest"},n.createElement(d,{src:"https://github.com/ory/keto/blob/"+a+"/contrib/docs-code-samples/"+t+"/curl.sh"})),n.createElement(o.Z,{value:"cli"},n.createElement(d,{src:"https://github.com/ory/keto/blob/"+a+"/contrib/docs-code-samples/"+t+"/cli.sh"}))),n.createElement(d,{src:"https://github.com/ory/keto/blob/"+a+"/contrib/docs-code-samples/"+t+"/expected_output.txt",title:"Result"}))}},58551:function(e,t,a){"use strict";a.r(t),a.d(t,{frontMatter:function(){return l},contentTitle:function(){return c},metadata:function(){return p},toc:function(){return u},default:function(){return h}});var n=a(22122),s=a(19756),o=(a(67294),a(3905)),r=a(57203),i=["components"],l={title:"Expand API: Display who has Access to an Object"},c=void 0,p={unversionedId:"guides/expand-api-display-who-has-access",id:"guides/expand-api-display-who-has-access",isDocsHomePage:!1,title:"Expand API: Display who has Access to an Object",description:"This guide will explain how to use Ory Keto's expand-API to display who has",source:"@site/docs/guides/expand-api-display-who-has-access.mdx",sourceDirName:"guides",slug:"/guides/expand-api-display-who-has-access",permalink:"/keto/docs/next/guides/expand-api-display-who-has-access",editUrl:"https://github.com/ory/keto/edit/master/docs/docs/guides/expand-api-display-who-has-access.mdx",tags:[],version:"current",lastUpdatedBy:"zepatrik",lastUpdatedAt:1633358296,formattedLastUpdatedAt:"10/4/2021",frontMatter:{title:"Expand API: Display who has Access to an Object"},sidebar:"docs",previous:{title:"List API: Display all Objects a User has Access to",permalink:"/keto/docs/next/guides/list-api-display-objects"},next:{title:"Going to Production",permalink:"/keto/docs/next/guides/production"}},u=[{value:"Example",id:"example",children:[{value:"Displaying who has Access",id:"displaying-who-has-access",children:[]},{value:"Maximum Tree Depth",id:"maximum-tree-depth",children:[]},{value:"Analyzing the Tree",id:"analyzing-the-tree",children:[]}]}],d={toc:u};function h(e){var t=e.components,a=(0,s.Z)(e,i);return(0,o.kt)("wrapper",(0,n.Z)({},d,a,{components:t,mdxType:"MDXLayout"}),(0,o.kt)("p",null,"This guide will explain how to use Ory Keto's expand-API to display who has\naccess to an object, and why. Please refer to the\n",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/reference/proto-api"},"gRPC")," and ",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/reference/rest-api"},"REST")," API\nreference documentation for all details. In general, the expand-API allows to\nexpand a given subject set into all its effective subjects."),(0,o.kt)("h2",{id:"example"},"Example"),(0,o.kt)("p",null,"As an example, we want to look at a file sharing application. Files are\nhierarchically organized in a directory structure. Every user owns files and\ndirectories, and can grant any other user access to them on a per-file or\nper-directory basis. Users can only see and access files they own or were\ngranted access by the owner."),(0,o.kt)("p",null,"Directories and files are stored in Ory Keto within the ",(0,o.kt)("inlineCode",{parentName:"p"},"directories")," and\n",(0,o.kt)("inlineCode",{parentName:"p"},"files")," namespaces respectively. They are identified by a UUID that the\napplication maps to the actual object metadata. Users are also identified by and\nmapped to a UUID."),(0,o.kt)("div",{className:"admonition admonition-info alert alert--info"},(0,o.kt)("div",{parentName:"div",className:"admonition-heading"},(0,o.kt)("h5",{parentName:"div"},(0,o.kt)("span",{parentName:"h5",className:"admonition-icon"},(0,o.kt)("svg",{parentName:"span",xmlns:"http://www.w3.org/2000/svg",width:"14",height:"16",viewBox:"0 0 14 16"},(0,o.kt)("path",{parentName:"svg",fillRule:"evenodd",d:"M7 2.3c3.14 0 5.7 2.56 5.7 5.7s-2.56 5.7-5.7 5.7A5.71 5.71 0 0 1 1.3 8c0-3.14 2.56-5.7 5.7-5.7zM7 1C3.14 1 0 4.14 0 8s3.14 7 7 7 7-3.14 7-7-3.14-7-7-7zm1 3H6v5h2V4zm0 6H6v2h2v-2z"}))),"info")),(0,o.kt)("div",{parentName:"div",className:"admonition-content"},(0,o.kt)("p",{parentName:"div"},"For the sake of readability, the code samples use the object path and user name\ninstead. Please refer to the ",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/concepts/objects"},"objects")," and\n",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/concepts/subjects"},"subjects")," pages for why the mapping is necessary."))),(0,o.kt)("h3",{id:"displaying-who-has-access"},"Displaying who has Access"),(0,o.kt)("p",null,"To assist users with managing permissions for their files, the application has\nto display exactly who has access to a file and why. In this example, we assume\nthat the application knows the following files and directories:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre"},"\u251c\u2500 photos            (owner: maureen; shared with laura)\n   \u251c\u2500 beach.jpg      (owner: maureen)\n   \u251c\u2500 mountains.jpg  (owner: laura)\n")),(0,o.kt)("p",null,"This is represented in Ory Keto by the following\n",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/concepts/relation-tuples"},"relation tuples"),":"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-keto-relation-tuples"},"// ownership\ndirectories:/photos#owner@maureen\nfiles:/photos/beach.jpg#owner@maureen\nfiles:/photos/mountains.jpg#owner@laura\n\n// maureen granted access to /photos to laura\ndirectories:/photos#access@laura\n\n// the following tuples are defined implicitly through subject set rewrites (not supported yet)\ndirectories:/photos#access@(directories:/photos#owner)\nfiles:/photos/beach.jpg#access@(files:/photos/beach.jpg#owner)\nfiles:/photos/beach.jpg#access@(directories:/photos#access)\nfiles:/photos/mountains.jpg#access@(files:/photos/mountains.jpg#owner)\nfiles:/photos/mountains.jpg#access@(directories:/photos#access)\n\n// the follwoing tuples are required to allow the subject set rewrites (not supported yet)\ndirectories:/photos#parent@(files:/photos/beach.jpg#_)\ndirectories:/photos#parent@(files:/photos/mountains.jpg#_)\n")),(0,o.kt)("p",null,"The user ",(0,o.kt)("inlineCode",{parentName:"p"},"maureen")," now wants to manage ",(0,o.kt)("inlineCode",{parentName:"p"},"access")," for the file\n",(0,o.kt)("inlineCode",{parentName:"p"},"/photos/beach.jpg"),". Therefore, the application uses the expand-API to get a\ntree of everyone who has access to that file:"),(0,o.kt)(r.Z,{sampleId:"expand-api-display-access/01-expand-beach",version:"v0.7.0-alpha.0.pre.3",mdxType:"CodeTabs"}),(0,o.kt)("h3",{id:"maximum-tree-depth"},"Maximum Tree Depth"),(0,o.kt)("p",null,"The ",(0,o.kt)("inlineCode",{parentName:"p"},"max-depth")," parameter is important to keep the request latency within an\nacceptable bound, but also abstract away the most basic subject sets. In many\ncases the application does not want to resolve all subject sets, but rather\nwants to display that e.g. ",(0,o.kt)("inlineCode",{parentName:"p"},"Everyone in the company")," or ",(0,o.kt)("inlineCode",{parentName:"p"},"Admins")," have a specific\nrelation."),(0,o.kt)("p",null,"In this example the application knows the rough structure of the relation tuples\nit uses and can therefore determine that ",(0,o.kt)("inlineCode",{parentName:"p"},"max-depth=3")," is sufficient to display\nall relevant relations:"),(0,o.kt)("ol",null,(0,o.kt)("li",{parentName:"ol"},"directly granted access (depth 1)"),(0,o.kt)("li",{parentName:"ol"},"indirectly granted access through ownership (depth 2)"),(0,o.kt)("li",{parentName:"ol"},"indirectly granted access through ownership of the parent (depth 3)")),(0,o.kt)("h3",{id:"analyzing-the-tree"},"Analyzing the Tree"),(0,o.kt)("p",null,"The tree does not only include the subject IDs (in this case usernames), but\nalso for what reason they were included. This can be useful for users to audits\npermissions. Also, in many cases the application would not want to list all\nsubject IDs but rather abstract away some subject sets."))}h.isMDXComponent=!0},83300:function(e,t){"use strict";var a=function(){if("undefined"!=typeof self)return self;if("undefined"!=typeof window)return window;if(void 0!==a)return a;throw new Error("unable to locate global object")}();e.exports=t=a.fetch,a.fetch&&(t.default=a.fetch.bind(a)),t.Headers=a.Headers,t.Request=a.Request,t.Response=a.Response}}]);