text: (self.webpackChunkdocusaurus_template=self.webpackChunkdocusaurus_template||[]).push([[7850],{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,u=e.values,p=e.groupId,d=e.className,h=n.Children.toArray(e.children),m=null!=u?u: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,b=o(),g=b.tabGroupChoices,k=b.setTabGroupChoices,v=(0,n.useState)(f),y=v[0],w=v[1],x=[];if(null!=p){var N=g[p];null!=N&&N!==y&&m.some((function(e){return e.value===N}))&&w(N)}var j=function(e){var t=e.currentTarget,a=x.indexOf(t),n=m[a].value;w(n),null!=p&&(k(p,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))},I=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:I,onFocus:j,onClick:j},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",u=a(25108),p=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=p(n,t);a>0&&(t=["// ..."].concat(t.slice(a,-1)));var o=p(s,t);return o>0&&(t=[].concat(t.slice(0,o+1),["// ..."])),t.join("\n")})).then(r).catch(u.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"}))}},24529:function(e,t,a){"use strict";a.r(t),a.d(t,{frontMatter:function(){return l},contentTitle:function(){return c},metadata:function(){return u},toc:function(){return p},default:function(){return h}});var n=a(22122),s=a(19756),o=(a(67294),a(3905)),r=a(57203),i=["components"],l={title:"List API: Display all Objects a User has Access to"},c=void 0,u={unversionedId:"guides/list-api-display-objects",id:"guides/list-api-display-objects",isDocsHomePage:!1,title:"List API: Display all Objects a User has Access to",description:"In this guide you will learn how to use Ory Keto's list API to display a list of",source:"@site/docs/guides/list-api-display-objects.mdx",sourceDirName:"guides",slug:"/guides/list-api-display-objects",permalink:"/keto/docs/next/guides/list-api-display-objects",editUrl:"https://github.com/ory/keto/edit/master/docs/docs/guides/list-api-display-objects.mdx",tags:[],version:"current",lastUpdatedBy:"zepatrik",lastUpdatedAt:1633511029,formattedLastUpdatedAt:"10/6/2021",frontMatter:{title:"List API: Display all Objects a User has Access to"},sidebar:"docs",previous:{title:"Check whether a User has Access to Something",permalink:"/keto/docs/next/guides/simple-access-check-guide"},next:{title:"Expand API: Display who has Access to an Object",permalink:"/keto/docs/next/guides/expand-api-display-who-has-access"}},p=[{value:"Example",id:"example",children:[{value:"Listing Objects",id:"listing-objects",children:[]},{value:"Listing Subjects",id:"listing-subjects",children:[]}]},{value:"Application Context",id:"application-context",children:[]},{value:"Pagination",id:"pagination",children:[]}],d={toc:p};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,"In this guide you will learn how to use Ory Keto's list API to display a list of\nall objects (e.g. files, ...) a user has access to. 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 list API allows you to\nquery relation tuples based on partial relation tuples."),(0,o.kt)("h2",{id:"example"},"Example"),(0,o.kt)("p",null,"As an example, we want to look at a chat application. Every user is part of one\nor more chats, and each chat has one or more members."),(0,o.kt)("p",null,"Chats are stored in Ory Keto within the ",(0,o.kt)("inlineCode",{parentName:"p"},"chats")," namespace. They are identified\nby a UUID that the application maps to the actual object metadata. Users are\nalso identified by and mapped 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 name of the chat and\nusername instead of the UUIDs. Please refer to the\n",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/concepts/objects"},"objects")," and ",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/concepts/subjects"},"subjects"),"\npages for why the mapping is necessary."))),(0,o.kt)("h3",{id:"listing-objects"},"Listing Objects"),(0,o.kt)("p",null,"Our example application allows users to browse the chats they belong to. To\nachieve that, it uses Ory Keto's list API."),(0,o.kt)("p",null,"We assume that the application currently has the following chats:"),(0,o.kt)("pre",null,(0,o.kt)("code",{parentName:"pre",className:"language-yml"},"memes:\n  members:\n    - PM\n    - Vincent\n    - Julia\ncars:\n  members:\n    - PM\n    - Julia\ncoffee-break:\n  members:\n    - PM\n    - Vincent\n    - Julia\n    - Patrik\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"},"chats:memes#member@PM\nchats:memes#member@Vincent\nchats:memes#member@Julia\n\nchats:cars#member@PM\nchats:cars#member@Julia\n\nchats:coffee-break#member@PM\nchats:coffee-break#member@Vincent\nchats:coffee-break#member@Julia\nchats:coffee-break#member@Patrik\n")),(0,o.kt)("p",null,"The user ",(0,o.kt)("inlineCode",{parentName:"p"},"PM")," now opens the chat application. To display a list of all of ",(0,o.kt)("inlineCode",{parentName:"p"},"PM"),"'s\nchats, the application uses Keto's list API:"),(0,o.kt)(r.Z,{sampleId:"list-api-display-objects/01-list-PM",version:"v0.7.0-alpha.0",mdxType:"CodeTabs"}),(0,o.kt)("p",null,"As a response, the application gets the list of all chats the user ",(0,o.kt)("inlineCode",{parentName:"p"},"PM")," is a\nmember of. It can then use the information to build the UI."),(0,o.kt)("h3",{id:"listing-subjects"},"Listing Subjects"),(0,o.kt)("p",null,"Another view of the chat application has to display all members of a specific\ngroup to the user. This can also be achieved using the list API. In cases where\nsuch a membership would be modeled through\n",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/concepts/subjects#subject-sets"},"subject sets"),", you have to use the\n",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/guides/expand-api-display-who-has-access"},"expand-API"),"."),(0,o.kt)("div",{className:"admonition admonition-caution alert alert--warning"},(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:"16",height:"16",viewBox:"0 0 16 16"},(0,o.kt)("path",{parentName:"svg",fillRule:"evenodd",d:"M8.893 1.5c-.183-.31-.52-.5-.887-.5s-.703.19-.886.5L.138 13.499a.98.98 0 0 0 0 1.001c.193.31.53.501.886.501h13.964c.367 0 .704-.19.877-.5a1.03 1.03 0 0 0 .01-1.002L8.893 1.5zm.133 11.497H6.987v-2.003h2.039v2.003zm0-3.004H6.987V5.987h2.039v4.006z"}))),"caution")),(0,o.kt)("div",{parentName:"div",className:"admonition-content"},(0,o.kt)("p",{parentName:"div"},"In this case the application should probably first use the\n",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/guides/simple-access-check-guide"},"check-API")," to check whether the user is\nallowed to list the members of a group. This step is not part of this example."))),(0,o.kt)("p",null,"In our example, a user wants to see who is a member of the ",(0,o.kt)("inlineCode",{parentName:"p"},"coffee-break")," group:"),(0,o.kt)(r.Z,{sampleId:"list-api-display-objects/02-list-coffee-break",version:"v0.7.0-alpha.0",mdxType:"CodeTabs"}),(0,o.kt)("h2",{id:"application-context"},"Application Context"),(0,o.kt)("p",null,"It is important to note that the list API does ",(0,o.kt)("strong",{parentName:"p"},"not")," expand\n",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/concepts/subjects#subject-sets"},"subject sets"),'. Usually the application\nhas some context to determine what tuples to query anyway. That could be e.g.\nknowledge of the structure of subject sets like depth or hierarchy, or the UI\ncontext, like a "My Items" view which should probably contain other objects than\na "My Organizations" or "Shared with Me" views. If there really is no way to\nnarrow down a query, you will have to use the\n',(0,o.kt)("a",{parentName:"p",href:"./expand-api-display-who-has-access"},"expand-API")," instead, or repeatedly call\nthe list API. Try to avoid such cases because they require a lot of resources\nand can quickly degrade the service quality for all users. Please refer to the\n",(0,o.kt)("a",{parentName:"p",href:"/keto/docs/next/performance"},"performance considerations"),"."),(0,o.kt)("h2",{id:"pagination"},"Pagination"),(0,o.kt)("p",null,"The list API only returns paginated results. Currently, it is not possible to\ncustomize the order of results. The response returns an opaque token that has to\nbe used to retrieve the following page. The first page can be retrieved by\npassing no, or an empty token."),(0,o.kt)("p",null,"The page size can be adjusted at any point, not only when requesting the first\npage. It defaults to 100 items."))}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}}]);