From de43d6d104d1008b51b137fc076bbb2665a2a049 Mon Sep 17 00:00:00 2001 From: Luka Zhupa <5446951+lukazhupa@users.noreply.github.com> Date: Thu, 22 Oct 2026 15:49:24 -0400 Subject: [PATCH 1/2] feat(accounts): a person's parent names its module's operation bindings StartTask takes operation_bindings: binding ids of the audience module. The host resolves each one the way the module's delegated exchange will (ModuleOperationAudience: the binding as installed at the organization, narrowed by that installation), adds its invoke scopes beside authority_scopes, keeps each resource only if the person holds it, and refuses by name a binding the person holds nothing under. So a consumer names the binding and never its resource ids, which come from the installation. Direct human-owned mints only. --- module/WORK_CONTEXTS.md | 12 ++ .../api/accounts/authority/contract.binpb | Bin 1199552 -> 1200272 bytes .../saas/accounts/v1/work_contexts.proto | 14 ++ .../api/accounts/connect/contract.binpb | Bin 1199552 -> 1200272 bytes .../saas/accounts/v1/work_contexts.proto | 14 ++ module/contracts/api/catalog.codefly.json | 4 +- module/module.package.codefly.yaml | 4 +- .../adapters/start_task_operation_bindings.go | 121 ++++++++++++++++++ .../start_task_operation_bindings_test.go | 105 +++++++++++++++ .../code/pkg/adapters/work_context_rpcs.go | 15 ++- .../gen/saas/accounts/v1/work_contexts.pb.go | 25 +++- .../generated/openapi-raw/api.swagger.json | 7 + .../accounts/openapi/api.swagger.json | 7 + .../saas/accounts/v1/work_contexts.proto | 14 ++ .../gen/saas/accounts/v1/work_contexts_pb.ts | 14 +- 15 files changed, 344 insertions(+), 12 deletions(-) create mode 100644 module/services/accounts/code/pkg/adapters/start_task_operation_bindings.go create mode 100644 module/services/accounts/code/pkg/adapters/start_task_operation_bindings_test.go diff --git a/module/WORK_CONTEXTS.md b/module/WORK_CONTEXTS.md index 5d7ef077e..ba4838ad8 100644 --- a/module/WORK_CONTEXTS.md +++ b/module/WORK_CONTEXTS.md @@ -187,6 +187,18 @@ The mode is the binding's, never the request's. The chain-preserving exchange, StartTask and registered-agent actors are unchanged, and a binding without a mode keeps its owner-only child. +**A parent that names its module's bindings.** A person's parent for a module +that will exchange through its installed bindings may name those bindings +instead of their scopes: `StartTask`'s `operation_bindings` takes binding ids +of the audience module. The host resolves each one the way the exchange will +(`ModuleOperationAudience`: the binding as installed at the organization, +narrowed by that installation) and adds its invoke scopes, keeping each +resource only if the person holds it by the check every mint makes. A binding +the person holds nothing under is refused by name. So a source binding's ids +come from the organization's installation, never from the caller. Only a +direct human-owned mint takes it; an actor's mint and a resource-confined mint +refuse it. A scope the caller also names is carried once. + Resource kinds, actions and resource ids are sorted and unique; wildcards are refused. Lookup scopes are optional: a binding that declares only invoke scopes redeems no lookup, and a lookup exchange under it is refused by name. When present, diff --git a/module/contracts/api/accounts/authority/contract.binpb b/module/contracts/api/accounts/authority/contract.binpb index 590c0a8eb1d2d00c9520361083a0d0e6c244e78f..9e7aba42afcbad452c7a94ab77c66ba54c249e49 100644 GIT binary patch delta 10295 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 JX*9Ox{{ay{G|2z} delta 9561 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 P0RIM7$A2;Ye%=2Ai9&AS diff --git a/module/contracts/api/accounts/authority/proto/saas/accounts/v1/work_contexts.proto b/module/contracts/api/accounts/authority/proto/saas/accounts/v1/work_contexts.proto index c5db6a13c..7b8356d96 100644 --- a/module/contracts/api/accounts/authority/proto/saas/accounts/v1/work_contexts.proto +++ b/module/contracts/api/accounts/authority/proto/saas/accounts/v1/work_contexts.proto @@ -91,6 +91,20 @@ message StartTaskWorkContextRequest { (buf.validate.field).string.min_len = 1, (buf.validate.field).string.max_len = 512 ]; + // Operation bindings of the audience module, by name, whose invoke scopes + // this capability carries beside authority_scopes. The host resolves each + // name to the binding as installed at org_id, narrowed by that installation + // exactly as the module's delegated exchange redeems it, and keeps each + // resource only if the person holds it. The caller names a binding and never + // its resource ids. A direct human-owned mint only. + repeated string operation_bindings = 11 [ + (buf.validate.field).repeated.max_items = 64, + (buf.validate.field).repeated.unique = true, + (buf.validate.field).repeated.items.string = { + min_len: 1 + max_len: 256 + } + ]; } // StartInstallationTaskRequest is the headless (no-user-present) mint path. The diff --git a/module/contracts/api/accounts/connect/contract.binpb b/module/contracts/api/accounts/connect/contract.binpb index 590c0a8eb1d2d00c9520361083a0d0e6c244e78f..9e7aba42afcbad452c7a94ab77c66ba54c249e49 100644 GIT binary patch delta 10295 zcmZ8nX_Qsfm98_q``){+9>N8S5)oc8MnN2KMbq6G9HK!x-5rx&-TkBEDym+jB2>Am z3XN-Z=Q5Pc2pHiZP)dPdf~XPHQZdS86h%M=5hFOXi6{sOCI%I?zrD}d_o6>)Exvuu z*=G;m-shbA*tm4e{H`&RI#h@5Fdeod(-CxJJ8~WQjoua%+8iQ%my=W1E^Ao0@NIt1VO}^~lxs(bxA%2CwjAm7WKyOWo6V zSLbR9udh&HHp*00ZMBujZ}j|O_j`w{-!$r`?&ptIkIBc?Va6(*w~=YkVYtqOmSAKX z^v`X-m8SHsvT;c^F17ru)IP3Us-M`{RBmo4mnO6{P8?qzSsHs=skwdv{&DNLmbP-K zsriCf-_*opwuvCtJQQy!${x(`qJ~>y0x0M>p|cW&F~ZZjS54b z#SF4o=p{%2MMDG`pyB6+MVId!GJM|8O&IqK^OW%1^n9cv(`YXYPm^Fx*kH~jRo^etczg*G}QSjfI>Ap004JRL)el~ffj|4ouqUvCSp}sMX1o1J=MA@ z6WPg1=iH#iAkcSYP!Y_Z5eqhiquv4$rOB$#x5Hit5>z2bJ!(jF=0Euv3fiZn+My2U z8X07muN4U8Q`Fi01w#20bdH7`x(YYwnL%jdxVzuH&FmMU0E3qXuNQ?Ky25c`i~Td?K;#2 z7lvnmPgtCz)fY`y#&2kdP*`%i0)JfiSOg!YDUgh4sI`TIGvHuMMi7nyz}bln78+)# zd@q@fO=qY+XZlSoHl3krW+$6k@)A4DRCa{lltoxJOWE_imJA%5qx4yx5ins+th6|m z!IpDWzu__&Yv-u*MtB%t_FSd2-rgVzfaWLm23R~-<%B;07SB~ReZ9Q_?47G>=O^|C zu-8E29kH^*yumEPx_Qce!*7~JrWW{2AxPOM0JW@tnc|5#JUx!M=^p6xGHgp!n;BW&u9bvE2QuM;nE6K zvnp|kN}CFAW$F@(ufd%vn(lh~Vqy%d69`?~9o{aJ=}zoEi=RD*HoMvthft&eRg zj#lv_LN?1}TPK+Tj4Y5N8xSm6Cu9SJ#&tqAKxAf}aE+kj+B)HyWv;E4%m79K(Bmq0 zZ5aaSUoT_>g#PtHHb8i~UJZZTdrAVAnMWlvUW8?j3D?k55R2|~Qym3BPxTaO}AU5q*HBTj*GT}gnC(=z>gk?_(9jvAUXoIvAz$gHECfU@0EgR(gv9Tsf zZczPQ7sqrc+n_GIh^q_`Hf&HAKa75%n#iM>L}O>z{_9IzD2 zHiMDHaFGEevB`E-^-lNy*y2JJZ&$ey+zm6hu)nNuZ=xGe2KOc)T#l$40Kt&cArixo zu1>he*&T9q%Fxwmhg_X9%=4YXbHK;~Ii3R%Cw2U9lRz$H$K(j_9ul=e!8(b_>xnO!U2}O9%zf zwJuOZbHKd2@n9~N?o|T@$|yYAt4d?IsbVxYJ?7isURC!4KZO{*PZ*scMgzT;xI~QJ zCyd50w%e!ryCVz`+wD{5k77at!oz*)g4YrUh0w1Gp);rn&*=Ta=nP+}4y0j44+DB5 zv4J>!Kq?utvFQP+WI$|sK-IjFY$`XZ*VCxVjp`5RNv;?(nyyp_rHlX>xiSL6mItM= z17gdAQbid}H>yKYMSxKN^mbw|-KY*p6=4{Q4@nh$4;Tg?Qnha<2FsP|O(`Rqb#ctG&uh%la@ zWBd_ee87x9DyadC0-)p4+981cqe6K==szly2ZXCf)$rrqRT4PHe<-=}A}l*5j1TBe zZBGy<=PL9D;3xq6o3j1>7jJv|qEP8uU7Q@BklP!kz>X6t>c^}Ggu)Z*oB>QsK-h6Y z4g6bThjjaoQahyE|5dtuz}@~N*|EUcf#LxC6n0EfFr?DASey}`l5 zo|V8@eM(pfh}EZrm4Gnrl&bwQF;05>=R!)F<4E}hlTv|{xYpa-b=87OSC8V(_;#H; zi{qbVPEJGExw!-41*NsS`2oR_X*%CS=E9O`x>w{OU0kQ9OEDM7JrT1^x9Qqnivh88 zy7q71ILobaM!FWQ5~$=A6Gdid-zqT|YiDTRDzlodRx{J37z8@gFQvrJ)c)OyxjgFM ztyykgv$T%ITVxH;J)tp*wX?Lp_5xz-^sgj9Xqv5~ z!QLp!;A}n2z522`h%tLk>H*ptkf@@bG)L!UE%l^1=~{Y@%}tkP84dCK(Bo{b&bxM# z)mbc^s|Sgu9QL_G>$Ah^m_0y80d#NAtfoiR9lHN8uZkX3cj$B7@sXu3cy?auJT3%4 zvTYtV&C`kVd1#ua{Sy&Op=qA36DvY!`sewnDqInO-0LQ*L#UdcstTcMzV=zI;F4B2N7eHuSq-$L<03s%f zbiXV46ej4p>>_=(dm81mg83D)o*rv}Q2=zeZg3Z92oU6o_TM!Cv0X*?7t^Q;D%wBZ zFbWqcdidSieb*p?yF)J4_=xd)axxzU_v)&rwd&Vh88x`rzjB~=aaDY;&be=GdG>a> zcncU=Aa|w!Vf*D$gn-z7xfCHFyj`yS2PlHh+vVDSfXefk(v>QQHlS<6n67>pW$eoo z5Zb$>OaY<2OZyuQL8rY-`wLB;d%%iRJ9+@n7)}%hp?rmK84$`>2$y4EXkVd+{D3n> zaC$yKYF6l>?)zAtkA#(yDZnTITCH88fRU(`x-islM}1?Z9yUrKwp*z$x|mNTOh%$s z>d~uxqP|NK_XX-c?Y}^I5u8{>$#W6HBhRiS8$Q;cOCG000QhkCKhzW_s(7ss9Rt{Y zt&Uvv0b={LRDJaQ3lQ6{)gvDE+mpZ@&JSw;Dawnm>>=&%O?mEE8)*0UGQ2x#3vg(^ z*D|HHa{c)3QMJWGdh`ZeaNjPl09I_!JzX0Hgt`rS@R0BWVqzEtn{?I7VB#Mty@nS1 zR*t_@ovr-$*~RDMO&T9q$>WgexLI-vi1&ri5eWJ>3mpNWf3wgL5Gma(bR_5qvsvgE zG99<1%ApPDI!8y0GS0itLV(b|MGth&07Cl~U3V=LhM?2FMGta}3;8aymBK|09x$>% zKlXl6zuzkP!XWnAs`2@oypDmf*;YN=^>9FJwpE{dz2A&4Ecmp$RiF1Gua7SC&j~w2 zVkgiGo}Iwh?zwb3V&`)@a%~Eu*zGyp{~V@AsAHn%b9%rFo*$Ut`SE+H4#1lui>ALNS4Z_hEQyb)R{}S17 zj7U|8g5A36%}V`%;)n5W?Z3Dcm_~af(}0l$a$OAwmh2H40m70!LL)%LeviH=o52Euq=aW?J~hCll9zYDZ;ec1Ovm~VL=vW6x{_$)&!TG7LawuK8Aqw<`!Y>Z(n?RP!y~)g~t#PSN%?iLukDThJ;a__n&6sUGpeK zf4d_s;=9r4o!y-yir49Qqe)5@zx~>cCMj7uls1|xf8x!cZ|s|l-I3(Qy1Z;Mg&j#= zScbf8PI<~AXx@_YltoCu*3)>(G79z!DNk93U~fx<%_0Q*#gwNkLabj(qsSt}dV3mc z79rLzr#!`{f>*%P4-%fzmF<fe1l@PrL^8~ev39vK&p-KOxz zBpxh-);%dtSp>~{nWvSHdsp{}_nF{8LQkH6=)9iNlSNF=H%#s_pK&LIrh_RxS%!$c znMRC7h}c^xJz0c^y={W?iHb2QfMp2TJ88&RgpmDT8dw&Ap6{mgq&WP^*!L59W;{Lr zWD4&m^mIbd^F1=dQP#=8&OV2Up3B^kf-io@jGk^Qw_jLX5+6&`5x~`BX*%dFe=JQ0 z9xTCeW2aa-Q398aktz@bUivf(NX9CJBDd1^)qCcqR!x%MkpvY4BMD%@3xrXA#BzAuwZI5_}dT`0LZ) zvxtNLn8hz&j%-c{m5-;HU>P#;M4AZ}ArnugnP3qz@l=wDShEbN*pQ}zMM%ZdX)0KR zRQxVY1qI|;Yd0sE$oWh>YYUr`OgJGjvC-m!B@X!Wbd#O_(@Tr^>w&*B@YfT6y}EyT JX*9Ox{{ay{G|2z} delta 9561 zcmZ8ndz6*cb)U1(x#!;d-MKtIa2Q5}8D68JDAHw3+L#!v2J>i>HmmI)U0q2RYt_{z zA@XQKs~-ashleo0fEN`|>ku(<43B}Eib0WA5enm@YIq3-Lo))3PZY%d_CDu)-^4#y z%ilg{@3SAjz0djXxcHGt&ptS5R;y~IR^4h^bFH>D-&$xbwnnX`?%GrP>EoX2nf8brlRet+&?WpSh7i)8Mw0~7)Kv#ZJrT>=Na&_DvYG2ptAF8F(wUdga-Gzt@ z#knap3#wbr)!t^3DTUEd6lp`nSpS&HQ7JO`?;3zoZB!fO7^pu&iLf-4ZVXGXH?`PbP(oCr0pmeou>dsqOVNO+Xs}jjEB32{k4+KrFLOy8r>F_7!~USg4t=84hslor(JfdH>Axi<8=CRSc2W@ z#Q_1q?({)}1A^V@m)z*>lD9j(?n{1Mn^@1F2ncB<9h({SQxn!~tdkj~O9R9)GlpFi z5RRE~&9zaPOF+5+%?Ur%{v3^SVr>66b4_0fy#9%~)*CPgXeI)3C!5&Jq`Sh&2D&qg z1F@E{b%V2J4!*>nWpLKay1T+zhGT-|e>da&St5bTf0(QNSvf>V`!9BpM448FOgXni1ht_aWuU&nT#s#&EJ%B2&dal%4XxKd8g z+_z?t^1J~QbYob=-bD(n%dub)W)~^6u7@DbB8A@N;IYcS)UgQ7>vo`dO*h zs$Wdt1Xhb725!*~1)h0bgw&zxZ}K7e{)TAiSNslU-LfnsBp0ZXM#R}&rqF86o`(5l z3a!Q*(rD|1_t#a@khrtaZDx5El8B&pUm%etV`H;Iq2ai>0R>&<&p>ly5iRGAM7;sg za}MU<@BIor=SuJ|x!3}(OdUqQVjXSgw?q4W&WCe2hZ5Yc>Q|=z=HM@<;DOM1nnVP( z2f6XO35+wbeN@$Kt}fB#i7I(Ckj)6$IzwpyV*}*K1_Vz!1K9v!vNMnk5S8f+WMk;a z))~lVh{?7lR0c4%KxWXS*#Hsg8r8hkN6G?MnV*NscoEbd3uMC( z!=aC7hf-{Tp77blvWgR|dp-a|`|-dBKpgtGs(T_mR0sz;{31J4L{R%>po1Y?GNvoc z6u{U5txFHpU`v<6J;Nn62{9+T)G#-ZupP#_)U^|&#DMUjOI^1v^}$~@=1Go{TbuXb z{()P+Qn+r!s!(!$prm0+0y#GO7b4C6l&d|WI4J|gOfO9tEzdq`i*>fxk|RG!Z;p>CRdEj zGYan@?yMZ%K|pv2ag0E4q%TAU1d3u5n`af?LZXPb5D*?iq9{Dy7I>axo&!0a12HGI z1)>AP(QSd~fFSy|Ky-$V=-UF(b3*jz0_Ol@3$)Xx4;WrQ7kCZ`^UnpI10sFTC8~L+ z&mT)1(YFWAc@gw>1fu7J=)1CzkP4vNeY`wWcd4Rd9QHzWml{4ItRkXas&SH#39E&+ zL@tV5s_AQf3p09mV04Zd4fK2(5;J;tU^JF-+-^0@U15MYZnwI6ybu}?5$;ykJf8*_ z2>suI(0FzPM(+uX&M`&I-Yl&cVnF*-ADGj7LnmW54&56%84!o=RdxH*L;W+#?8~z1 z-%;j&Fq2#}<_Iq-b0D-4pdi;qKsfS1nCyT!@<8aK9N{%(dO{Zg#un(M)L%TOuVV{HsU?5jb7x*-osslLk_E&cUAS$+VXyt999KaWGj?E z68Hfa8z4t{Aoy}5Fdh)T90`mE1V4@h#xrz`KN1*kh4Dv2X#isjbUci92w?wcpgbV# z9}ScTM5sqq^Kl<43moHL54G_ks2vN8w|FMU<^*$cp~7qcjxFFxWg6~k{L3lR?)cFU zD<}JvTapvu^@c6*KTfC-CsRMdw0|@6BTW0>hiPwR+V`>_ zOPn8Q4!~3JW0ry=m6QF;bCcfC9oP>idc(K}gcH4C+ylaiUUl86)QQjuZ-q{b=hmH79DmDzqKuo5t~KxaKGfuVgSuo4j3X96n$;oKQje>Qb4 z%=WhfDS3}0nU@L|fA44|yy%hW+0Ul$Z5?h7!_RRgRKtI)j=p1oLv?gh@f z)6HS?r%W-v%ifZSIUkU;;H1PDu?s(4gTg{4o`SoiwOlTEPuVip0$8&ELCGwGr# z23?*>7c*V{9Q!O&%?lds&&fmeGgWlsCQo^&ex^nSO9h-WiwK|Ai9~>j0_fhrdE$?% zSu|{{H&uY{EV{y7A9*Sw_!dfo$AbVU99x7>EtCddgryeppNLR}r50)mUPQ3;joFzg zJQ0B0*G--xn3|oLiePFs`7b@J!qjZSm);ttH2R>8YVNJ%FDZ8>ZB%eJ3c~2Q7$j0C zz}NuYB5?u1;#?{^x&XrBT&j1?0EnE-rG}g37G~(C>|DCkeH#@>!TovcJ%1_y#ujJ+ zO?D4x2$1A?aD7fu-cXP5}|`Qu1G? z3|+iS$$y;|{{!PQsS??hKq$$c2Se9AG z6abp!j69pM7si*7XLAA!>&s~L*Q8bq+lm&&Sw>@S^9K0Av^>jk!m!wJY6NL1J$vB<5yGc`VSDtujc;aZ%jZOznaE9;*V#6yPh8+ z|HFzGLG5AkFQ+2$%5hSze9Qzw7#U#k}z%13Ep7nR(v7f^r~T{OTA zVnCSdqEVxxUglyH+l^H747hlxa=f`bq%wARd4IBz@M|dt8wnG;LpcHQaS@mZ1k>Gt ziGVQO9heA+YIX-EGIU(&4or-MiJLOxum<#X$3(0OF1p`@0AYO-jc{=Q!ulp^x?Q-! z&{^L^BOTcydB|+$O!0IFj1ACV`?z@6Zw{rxBF@@Ov5PtZ#$lVO*$r?&9JZOR_>MnJ zUMu*?XER;-ZEuf>Sq;RDn3zCMd13i78wS>PJ*`4FKOL2Ylya*2PxH9dUio^q#wSAgGXIHDRS|76M?cTS%AkKdctc*oSn zY5zRA@t-OeMwMT_sVCgRP=~dia0>$>x*i($ZT#k3fa}bxp)9%TucB9xP+Jq*SEy!l zW#E|dD}!F4LPPWhjeft)f57Ja9BdK_cN*|=D5E)W&DocfkVEJ^G z3K1a{XUJCGySzLwc{>YV))4+XSrj5d_-C{5MTGF*&B7NE!hi2`iU|LG-gVdr5Ab=` zqG_Sl4SP-F%|H6#)agH%T%B`8`CF8lW_TMcFXadZC(;rk%b{C4w5{m8=e9_Z zrWM+bhz3Qapqy72jSuL;wZ5TbAE)j?UAVljE>gED%YbY{>Q-xeRk|yIGsxXTS?**T za`&*dmoALr90!Ph+uhB##0fY0Gs>pRAij&r!t<349VV-C0j&D_R|?p zMTA`cCd;CTkn62ku0@1gKa=qkzY}Z+PYUK`N+9^?9ioG(|m{wR(EDR6%j1&5}sB*98}vc*{$u~l%BEy+1Z!TQ$#|~{km|2 zuecM!(t(VgB16Vr$TB7(WbDO^o+3iVUefkzrecB)5E)YTa+Wd?A!YxYC00bB=PMaK zIS;RCb10=}&eQWXT{@J~(+NS(-?AHyvQ7qe4nD~ATg;)5p5<-HGJ~l4Dsp zEJ8h&m4iRzk7eb+w~9Tk&HHINtSiTHU3xz)hsaQlH?new2$p{*<&drs5l@X1I>A|lk~9bXq)laRJqyhM;@e$(h?}s{m@H($) znF%wUe;YZkt^Aq;`f%r-*Y$tqCqDmFa$ev1x4u{KM{#>Wn|2fKxV$UQ1zl=4;f@m- ztbUpmCx0vNgXxP|bz~b?=QC~Rm@v~tisH1Ibf$|)igQn8!u91rNt;Q>xU8Wb^GtdJ ziU{?XZ_+U?BGhAnaecyZvO_4xy(XRFB0@P9n)IF&5z4X1q*FX^#^YWyrUN-}V?6Kc zvDlP4kObEgPKbIeshow+gO(ap;6W|-DxSgseBFJFP^)S7N;>b*GhZLR4ad!2>G<;5%cZX&e8=GXC|$zyA0) P0RIM7$A2;Ye%=2Ai9&AS diff --git a/module/contracts/api/accounts/connect/proto/saas/accounts/v1/work_contexts.proto b/module/contracts/api/accounts/connect/proto/saas/accounts/v1/work_contexts.proto index c5db6a13c..7b8356d96 100644 --- a/module/contracts/api/accounts/connect/proto/saas/accounts/v1/work_contexts.proto +++ b/module/contracts/api/accounts/connect/proto/saas/accounts/v1/work_contexts.proto @@ -91,6 +91,20 @@ message StartTaskWorkContextRequest { (buf.validate.field).string.min_len = 1, (buf.validate.field).string.max_len = 512 ]; + // Operation bindings of the audience module, by name, whose invoke scopes + // this capability carries beside authority_scopes. The host resolves each + // name to the binding as installed at org_id, narrowed by that installation + // exactly as the module's delegated exchange redeems it, and keeps each + // resource only if the person holds it. The caller names a binding and never + // its resource ids. A direct human-owned mint only. + repeated string operation_bindings = 11 [ + (buf.validate.field).repeated.max_items = 64, + (buf.validate.field).repeated.unique = true, + (buf.validate.field).repeated.items.string = { + min_len: 1 + max_len: 256 + } + ]; } // StartInstallationTaskRequest is the headless (no-user-present) mint path. The diff --git a/module/contracts/api/catalog.codefly.json b/module/contracts/api/catalog.codefly.json index 5a4135c62..677541b10 100644 --- a/module/contracts/api/catalog.codefly.json +++ b/module/contracts/api/catalog.codefly.json @@ -10,7 +10,7 @@ "kind": "protobuf", "package": "saas.accounts.v1", "path": "contracts/api/accounts/authority/contract.binpb", - "digest": "sha256:373a8f3a22b7968c36dd9faffb9be5ef64213b1de2a532f5e567007bd65caaa7", + "digest": "sha256:21f848eb59ce4aca4903d354b66131c8e9595771d505d762fa07e730f32c4c4c", "services": [ { "name": "ModuleCapabilitiesService", @@ -65,7 +65,7 @@ "kind": "protobuf", "package": "saas.accounts.v1", "path": "contracts/api/accounts/connect/contract.binpb", - "digest": "sha256:373a8f3a22b7968c36dd9faffb9be5ef64213b1de2a532f5e567007bd65caaa7", + "digest": "sha256:21f848eb59ce4aca4903d354b66131c8e9595771d505d762fa07e730f32c4c4c", "services": [ { "name": "APIKeyService", diff --git a/module/module.package.codefly.yaml b/module/module.package.codefly.yaml index 34ea2ee34..d0b76c941 100644 --- a/module/module.package.codefly.yaml +++ b/module/module.package.codefly.yaml @@ -18,12 +18,12 @@ services: kind: protobuf package: saas.accounts.v1 path: contracts/api/accounts/authority/contract.binpb - digest: sha256:373a8f3a22b7968c36dd9faffb9be5ef64213b1de2a532f5e567007bd65caaa7 + digest: sha256:21f848eb59ce4aca4903d354b66131c8e9595771d505d762fa07e730f32c4c4c - endpoint: connect kind: protobuf package: saas.accounts.v1 path: contracts/api/accounts/connect/contract.binpb - digest: sha256:373a8f3a22b7968c36dd9faffb9be5ef64213b1de2a532f5e567007bd65caaa7 + digest: sha256:21f848eb59ce4aca4903d354b66131c8e9595771d505d762fa07e730f32c4c4c - name: auth-gateway endpoints: [grpc, rest] api-contracts: diff --git a/module/services/accounts/code/pkg/adapters/start_task_operation_bindings.go b/module/services/accounts/code/pkg/adapters/start_task_operation_bindings.go new file mode 100644 index 000000000..14dd0d9ab --- /dev/null +++ b/module/services/accounts/code/pkg/adapters/start_task_operation_bindings.go @@ -0,0 +1,121 @@ +package adapters + +import ( + "context" + "slices" + + accountsauth "accounts/pkg/auth" + "accounts/pkg/business" + gen "accounts/pkg/gen/saas/accounts/v1" + + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" + "google.golang.org/protobuf/proto" +) + +// requireOperationBindingMint refuses operation_bindings outside the one shape +// that takes them: a direct human-owned mint. An actor's ceiling and a confined +// credential's consumer ceiling are decided on the scopes a caller names, so +// neither is widened by a name resolved here. +func requireOperationBindingMint(ctx context.Context, req *gen.StartTaskWorkContextRequest) error { + if len(req.GetOperationBindings()) == 0 { + return nil + } + if req.GetActorPrincipalId() != "" { + return status.Error(codes.InvalidArgument, "operation_bindings is for a direct human-owned mint, not an actor's") + } + if _, confined := accountsauth.VerifiedResourceConfinement(ctx); confined { + return status.Error(codes.InvalidArgument, "operation_bindings is refused on a resource-confined mint") + } + if service == nil { + return status.Error(codes.Unavailable, "module authority unavailable") + } + return nil +} + +// withOperationBindingScopes adds the invoke scopes of each named operation +// binding of the audience module to the scopes a caller asked for. +// +// A name resolves through service.ModuleOperationAudience, the resolution the +// module's delegated exchange redeems the binding through: the binding as +// installed at the organisation, narrowed by that installation. So the resource +// ids come from the installation, never from the caller, and the capability +// carries exactly what the exchange will ask of it. Each resource is kept only +// if the person holds it, by the same check every mint makes; a binding the +// person holds nothing under is refused by name. A scope the caller already +// named is not added twice. +func (s *WorkContextAuthorityServer) withOperationBindingScopes(ctx context.Context, orgID, audience string, asked []*gen.WorkContextScope, names []string) ([]*gen.WorkContextScope, error) { + ownerID, err := s.authorizeOwner(ctx, orgID) + if err != nil { + return nil, err + } + module := business.ModuleCaller{PrincipalID: business.ModulePrincipalID(audience), BoundOrg: orgID} + out := slices.Clone(asked) + for _, name := range names { + binding, err := service.ModuleOperationAudience(ctx, module, orgID, audience, name) + if err != nil { + return nil, err + } + held := 0 + for _, scope := range binding.InvokeScopes { + kept, err := s.heldBindingScope(ctx, orgID, ownerID, audience, scope) + if err != nil { + return nil, err + } + if kept == nil { + continue + } + held++ + if !slices.ContainsFunc(out, func(existing *gen.WorkContextScope) bool { return proto.Equal(existing, kept) }) { + out = append(out, kept) + } + } + if held == 0 { + return nil, status.Errorf(codes.PermissionDenied, "the person holds nothing under operation binding %q", name) + } + } + return out, nil +} + +// heldBindingScope is the part of one binding scope the person holds: every +// action of the scope, on each resource it names, or on its kind when it names +// none. Nil when the person holds none of it. +func (s *WorkContextAuthorityServer) heldBindingScope(ctx context.Context, orgID, ownerID, audience string, scope business.ModuleOperationScope) (*gen.WorkContextScope, error) { + candidate := func(ids []string) *gen.WorkContextScope { + return &gen.WorkContextScope{ResourceKind: scope.ResourceKind, Actions: slices.Clone(scope.Actions), ResourceIds: ids} + } + holds := func(c *gen.WorkContextScope) (bool, error) { + permissions, _, err := workContextScopes([]*gen.WorkContextScope{c}, mintContentReads(audience)) + if err != nil { + return false, status.Error(codes.Internal, err.Error()) + } + _, err = s.resolveAuthority(ctx, orgID, ownerID, "", permissions) + switch status.Code(err) { + case codes.OK: + return true, nil + case codes.PermissionDenied: + return false, nil + } + return false, err + } + if len(scope.ResourceIDs) == 0 { + if ok, err := holds(candidate(nil)); !ok || err != nil { + return nil, err + } + return candidate(nil), nil + } + var ids []string + for _, id := range scope.ResourceIDs { + ok, err := holds(candidate([]string{id})) + if err != nil { + return nil, err + } + if ok { + ids = append(ids, id) + } + } + if len(ids) == 0 { + return nil, nil + } + return candidate(ids), nil +} diff --git a/module/services/accounts/code/pkg/adapters/start_task_operation_bindings_test.go b/module/services/accounts/code/pkg/adapters/start_task_operation_bindings_test.go new file mode 100644 index 000000000..b06b2ebad --- /dev/null +++ b/module/services/accounts/code/pkg/adapters/start_task_operation_bindings_test.go @@ -0,0 +1,105 @@ +package adapters + +import ( + "context" + "maps" + "testing" + + "accounts/pkg/auth" + "accounts/pkg/business" + gen "accounts/pkg/gen/saas/accounts/v1" + + corework "github.com/codefly-dev/core/workcontext" + "github.com/google/uuid" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +var bindingRunScope = []*gen.WorkContextScope{{ResourceKind: "runs", Actions: []string{"execute"}}} + +func (c moduleActorCase) startTaskNaming(bindings ...string) (*gen.IssuedWorkContext, error) { + caller := stampVerifiedIdentity(context.Background(), readOwner, readOrg, auth.Assurance{}) + return c.server.StartTask(caller, &gen.StartTaskWorkContextRequest{ + OrgId: readOrg, TaskId: uuid.NewString(), Audience: "reader", + AuthorityScopes: bindingRunScope, OperationBindings: bindings, + }) +} + +// A parent that names its module's binding carries that binding's installed +// scopes, resource ids included, without the caller naming them; and the +// module's exchange of that binding redeems it. +func TestStartTaskCarriesANamedBindingAsInstalled(t *testing.T) { + c := moduleActorFixture(t, moduleActorScopes) + parent, err := c.startTaskNaming("act-source") + require.NoError(t, err) + decoded, err := corework.Decode(parent.Token) + require.NoError(t, err) + scopes := wireScopes(decoded.Context().AuthorityScopes) + require.Len(t, scopes, 2) + require.Equal(t, "runs", scopes[0].ResourceKind) + require.Equal(t, business.SourceOperationResourceKind, scopes[1].ResourceKind) + require.Equal(t, []string{"invoke", "read"}, scopes[1].Actions) + require.Equal(t, []string{actorSource}, scopes[1].ResourceIds) + + issued, err := c.exchange("act-source", parent.Token, c.moduleToken, false) + require.NoError(t, err) + child, err := corework.Decode(issued.Token) + require.NoError(t, err) + require.Equal(t, []string{actorSource}, child.Context().ActorChain[0].GrantedScopes[0].ResourceIds) +} + +// A caller that also names the binding's scope gets it once. +func TestStartTaskDoesNotRepeatANamedScope(t *testing.T) { + c := moduleActorFixture(t, moduleActorScopes) + caller := stampVerifiedIdentity(context.Background(), readOwner, readOrg, auth.Assurance{}) + explicit := append(append([]*gen.WorkContextScope(nil), bindingRunScope...), + &gen.WorkContextScope{ResourceKind: business.SourceOperationResourceKind, Actions: []string{"invoke", "read"}, ResourceIds: []string{actorSource}}) + parent, err := c.server.StartTask(caller, &gen.StartTaskWorkContextRequest{ + OrgId: readOrg, TaskId: uuid.NewString(), Audience: "reader", AuthorityScopes: explicit, OperationBindings: []string{"act-source"}, + }) + require.NoError(t, err) + decoded, err := corework.Decode(parent.Token) + require.NoError(t, err) + require.Len(t, decoded.Context().AuthorityScopes, 2) +} + +func TestStartTaskOperationBindingRefusals(t *testing.T) { + t.Run("a binding the module is not installed with", func(t *testing.T) { + c := moduleActorFixture(t, moduleActorScopes) + _, err := c.startTaskNaming("no-such-binding") + require.Equal(t, codes.PermissionDenied, status.Code(err)) + require.Contains(t, status.Convert(err).Message(), "installed operation audience required") + }) + t.Run("an installation narrowed away from the binding's sources", func(t *testing.T) { + c := moduleActorFixture(t, moduleActorScopes) + live := c.f.live[business.ModulePrincipalID("reader")] + scope := live.Scope + scope.Follows = false + scope.OperationAudiences = maps.Clone(scope.OperationAudiences) + narrowed := scope.OperationAudiences["act-source"] + elsewhere := []business.ModuleOperationScope{{ResourceKind: business.SourceOperationResourceKind, Actions: []string{"invoke", "read"}, ResourceIDs: []string{"source-b"}}} + narrowed.InvokeScopes, narrowed.LookupScopes = elsewhere, elsewhere + scope.OperationAudiences["act-source"] = narrowed + live.Scope = scope + _, err := c.startTaskNaming("act-source") + require.Equal(t, codes.PermissionDenied, status.Code(err)) + require.Contains(t, status.Convert(err).Message(), "installed operation audience required") + }) + t.Run("a binding the person holds nothing under, refused by name", func(t *testing.T) { + c := moduleActorFixture(t, moduleActorScopes) + c.server.authority = sourceAccessWithdrawn{} + _, err := c.startTaskNaming("act-source") + require.Equal(t, codes.PermissionDenied, status.Code(err)) + require.Contains(t, status.Convert(err).Message(), `"act-source"`) + }) + t.Run("an actor's mint", func(t *testing.T) { + c := moduleActorFixture(t, moduleActorScopes) + caller := stampVerifiedIdentity(context.Background(), readOwner, readOrg, auth.Assurance{}) + _, err := c.server.StartTask(caller, &gen.StartTaskWorkContextRequest{ + OrgId: readOrg, TaskId: uuid.NewString(), Audience: "reader", ActorPrincipalId: uuid.NewString(), + AuthorityScopes: bindingRunScope, OperationBindings: []string{"act-source"}, + }) + require.Equal(t, codes.InvalidArgument, status.Code(err)) + }) +} diff --git a/module/services/accounts/code/pkg/adapters/work_context_rpcs.go b/module/services/accounts/code/pkg/adapters/work_context_rpcs.go index debe897a8..8a833604f 100644 --- a/module/services/accounts/code/pkg/adapters/work_context_rpcs.go +++ b/module/services/accounts/code/pkg/adapters/work_context_rpcs.go @@ -541,6 +541,10 @@ func (s *WorkContextAuthorityServer) StartTask( if err := requireVocabularyAudience(ctx, req.GetAudience()); err != nil { return nil, err } + if err := requireOperationBindingMint(ctx, req); err != nil { + return nil, err + } + authorityScopes := req.GetAuthorityScopes() eligible := mintContentReads(req.GetAudience()) var standing []string if service != nil && req.GetActorPrincipalId() == "" { @@ -551,8 +555,15 @@ func (s *WorkContextAuthorityServer) StartTask( return nil, err } defer release() + // Inside the authority span, so the installation the bindings resolve + // against is the one this capability is signed under. + if names := req.GetOperationBindings(); len(names) > 0 { + if authorityScopes, err = s.withOperationBindingScopes(ctx, req.GetOrgId(), req.GetAudience(), authorityScopes, names); err != nil { + return nil, err + } + } var kinds []string - for _, scope := range req.GetAuthorityScopes() { + for _, scope := range authorityScopes { if len(scope.GetActions()) == 1 && scope.GetActions()[0] == "read" { kinds = append(kinds, scope.GetResourceKind()) } @@ -565,7 +576,7 @@ func (s *WorkContextAuthorityServer) StartTask( direct := eligible eligible = func(kind string) bool { return direct(kind) || slices.Contains(foreign, kind) } } - permissions, scopes, err := workContextScopes(req.GetAuthorityScopes(), eligible) + permissions, scopes, err := workContextScopes(authorityScopes, eligible) if err != nil { return nil, status.Error(codes.InvalidArgument, err.Error()) } diff --git a/module/services/accounts/code/pkg/gen/saas/accounts/v1/work_contexts.pb.go b/module/services/accounts/code/pkg/gen/saas/accounts/v1/work_contexts.pb.go index 6de6b4e1a..5a8113a74 100644 --- a/module/services/accounts/code/pkg/gen/saas/accounts/v1/work_contexts.pb.go +++ b/module/services/accounts/code/pkg/gen/saas/accounts/v1/work_contexts.pb.go @@ -173,9 +173,16 @@ type StartTaskWorkContextRequest struct { // capability to this exact workspace and reject cross-workspace reuse. WorkspaceId *string `protobuf:"bytes,9,opt,name=workspace_id,json=workspaceId,proto3,oneof" json:"workspace_id,omitempty"` // Optional product/project attribution inside workspace_id. - ProjectId *string `protobuf:"bytes,10,opt,name=project_id,json=projectId,proto3,oneof" json:"project_id,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + ProjectId *string `protobuf:"bytes,10,opt,name=project_id,json=projectId,proto3,oneof" json:"project_id,omitempty"` + // Operation bindings of the audience module, by name, whose invoke scopes + // this capability carries beside authority_scopes. The host resolves each + // name to the binding as installed at org_id, narrowed by that installation + // exactly as the module's delegated exchange redeems it, and keeps each + // resource only if the person holds it. The caller names a binding and never + // its resource ids. A direct human-owned mint only. + OperationBindings []string `protobuf:"bytes,11,rep,name=operation_bindings,json=operationBindings,proto3" json:"operation_bindings,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *StartTaskWorkContextRequest) Reset() { @@ -271,6 +278,13 @@ func (x *StartTaskWorkContextRequest) GetProjectId() string { return "" } +func (x *StartTaskWorkContextRequest) GetOperationBindings() []string { + if x != nil { + return x.OperationBindings + } + return nil +} + // StartInstallationTaskRequest is the headless (no-user-present) mint path. The // caller is a trusted internal service (a job consumer leasing an ingest job), // not a bearer: owner := the installation's owner of record; actor := the agent @@ -1201,7 +1215,7 @@ const file_saas_accounts_v1_work_contexts_proto_rawDesc = "" + "\rresource_kind\x18\x01 \x01(\tB\n" + "\xbaH\ar\x05\x10\x01\x18\x80\x01R\fresourceKind\x12/\n" + "\aactions\x18\x02 \x03(\tB\x15\xbaH\x12\x92\x01\x0f\b\x01\x10@\x18\x01\"\ar\x05\x10\x01\x18\x80\x01R\aactions\x127\n" + - "\fresource_ids\x18\x03 \x03(\tB\x14\xbaH\x11\x92\x01\x0e\x10\x80\x02\x18\x01\"\ar\x05\x10\x01\x18\x80\x04R\vresourceIds\"\xa8\x04\n" + + "\fresource_ids\x18\x03 \x03(\tB\x14\xbaH\x11\x92\x01\x0e\x10\x80\x02\x18\x01\"\ar\x05\x10\x01\x18\x80\x04R\vresourceIds\"\xec\x04\n" + "\x1bStartTaskWorkContextRequest\x12\x1f\n" + "\x06org_id\x18\x01 \x01(\tB\b\xbaH\x05r\x03\xb0\x01\x01R\x05orgId\x12$\n" + "\atask_id\x18\x02 \x01(\tB\v\xbaH\b\xd8\x01\x01r\x03\xb0\x01\x01R\x06taskId\x12,\n" + @@ -1219,7 +1233,8 @@ const file_saas_accounts_v1_work_contexts_proto_rawDesc = "" + "\n" + "project_id\x18\n" + " \x01(\tB\n" + - "\xbaH\ar\x05\x10\x01\x18\x80\x04H\x01R\tprojectId\x88\x01\x01B\x0f\n" + + "\xbaH\ar\x05\x10\x01\x18\x80\x04H\x01R\tprojectId\x88\x01\x01\x12B\n" + + "\x12operation_bindings\x18\v \x03(\tB\x13\xbaH\x10\x92\x01\r\x10@\x18\x01\"\ar\x05\x10\x01\x18\x80\x02R\x11operationBindingsB\x0f\n" + "\r_workspace_idB\r\n" + "\v_project_idJ\x04\b\x03\x10\x04R\n" + "session_id\"\xab\x04\n" + diff --git a/module/services/accounts/generated/openapi-raw/api.swagger.json b/module/services/accounts/generated/openapi-raw/api.swagger.json index 92a8f0b83..1beb48c30 100644 --- a/module/services/accounts/generated/openapi-raw/api.swagger.json +++ b/module/services/accounts/generated/openapi-raw/api.swagger.json @@ -11870,6 +11870,13 @@ "projectId": { "type": "string", "description": "Optional product/project attribution inside workspace_id." + }, + "operationBindings": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Operation bindings of the audience module, by name, whose invoke scopes\nthis capability carries beside authority_scopes. The host resolves each\nname to the binding as installed at org_id, narrowed by that installation\nexactly as the module's delegated exchange redeems it, and keeps each\nresource only if the person holds it. The caller names a binding and never\nits resource ids. A direct human-owned mint only." } }, "description": "StartTaskWorkContextRequest mints a Task capability for the caller. Its Session\nis the one the caller authenticated with — or a fresh one when it has none —\nassigned by the issuer and returned in IssuedWorkContext, never named by the\ncaller: a session a caller could name is one it could name after another\ncredential's, and a confined credential is paired with a capability on the\nsession alone." diff --git a/module/services/accounts/openapi/api.swagger.json b/module/services/accounts/openapi/api.swagger.json index 9fb8507fd..66859d30b 100644 --- a/module/services/accounts/openapi/api.swagger.json +++ b/module/services/accounts/openapi/api.swagger.json @@ -5362,6 +5362,13 @@ }, "type": "array" }, + "operationBindings": { + "description": "Operation bindings of the audience module, by name, whose invoke scopes\nthis capability carries beside authority_scopes. The host resolves each\nname to the binding as installed at org_id, narrowed by that installation\nexactly as the module's delegated exchange redeems it, and keeps each\nresource only if the person holds it. The caller names a binding and never\nits resource ids. A direct human-owned mint only.", + "items": { + "type": "string" + }, + "type": "array" + }, "orgId": { "type": "string" }, diff --git a/module/services/accounts/proto/saas/accounts/v1/work_contexts.proto b/module/services/accounts/proto/saas/accounts/v1/work_contexts.proto index c5db6a13c..7b8356d96 100644 --- a/module/services/accounts/proto/saas/accounts/v1/work_contexts.proto +++ b/module/services/accounts/proto/saas/accounts/v1/work_contexts.proto @@ -91,6 +91,20 @@ message StartTaskWorkContextRequest { (buf.validate.field).string.min_len = 1, (buf.validate.field).string.max_len = 512 ]; + // Operation bindings of the audience module, by name, whose invoke scopes + // this capability carries beside authority_scopes. The host resolves each + // name to the binding as installed at org_id, narrowed by that installation + // exactly as the module's delegated exchange redeems it, and keeps each + // resource only if the person holds it. The caller names a binding and never + // its resource ids. A direct human-owned mint only. + repeated string operation_bindings = 11 [ + (buf.validate.field).repeated.max_items = 64, + (buf.validate.field).repeated.unique = true, + (buf.validate.field).repeated.items.string = { + min_len: 1 + max_len: 256 + } + ]; } // StartInstallationTaskRequest is the headless (no-user-present) mint path. The diff --git a/module/services/frontend/code/src/gen/saas/accounts/v1/work_contexts_pb.ts b/module/services/frontend/code/src/gen/saas/accounts/v1/work_contexts_pb.ts index 1ae3625b3..83a0d4391 100644 --- a/module/services/frontend/code/src/gen/saas/accounts/v1/work_contexts_pb.ts +++ b/module/services/frontend/code/src/gen/saas/accounts/v1/work_contexts_pb.ts @@ -15,7 +15,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file saas/accounts/v1/work_contexts.proto. */ export const file_saas_accounts_v1_work_contexts: GenFile = /*@__PURE__*/ - fileDesc("CiRzYWFzL2FjY291bnRzL3YxL3dvcmtfY29udGV4dHMucHJvdG8SEHNhYXMuYWNjb3VudHMudjEiiQEKEFdvcmtDb250ZXh0U2NvcGUSIQoNcmVzb3VyY2Vfa2luZBgBIAEoCUIKukgHcgUQARiAARImCgdhY3Rpb25zGAIgAygJQhW6SBKSAQ8IARBAGAEiB3IFEAEYgAESKgoMcmVzb3VyY2VfaWRzGAMgAygJQhS6SBGSAQ4QgAIYASIHcgUQARiABCK6AwobU3RhcnRUYXNrV29ya0NvbnRleHRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESHAoHdGFza19pZBgCIAEoCUILukgI2AEBcgOwAQESGgoSYWN0b3JfcHJpbmNpcGFsX2lkGAQgASgJEkgKEGF1dGhvcml0eV9zY29wZXMYBSADKAsyIi5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0U2NvcGVCCrpIB5IBBAgBEEASHAoIYXVkaWVuY2UYBiABKAlCCrpIB3IFEAEYgAQSQAoNcmVwbGF5X3BvbGljeRgHIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYCCABKAVCCrpIBxoFGIQHKAASJQoMd29ya3NwYWNlX2lkGAkgASgJQgq6SAdyBRABGIAESACIAQESIwoKcHJvamVjdF9pZBgKIAEoCUIKukgHcgUQARiABEgBiAEBQg8KDV93b3Jrc3BhY2VfaWRCDQoLX3Byb2plY3RfaWRKBAgDEARSCnNlc3Npb25faWQivwMKHFN0YXJ0SW5zdGFsbGF0aW9uVGFza1JlcXVlc3QSGAoGb3JnX2lkGAEgASgJQgi6SAVyA7ABARIhCg9pbnN0YWxsYXRpb25faWQYAiABKAlCCLpIBXIDsAEBEhkKB3Rhc2tfaWQYAyABKAlCCLpIBXIDsAEBEkgKEGF1dGhvcml0eV9zY29wZXMYBSADKAsyIi5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0U2NvcGVCCrpIB5IBBAgBEEASHAoIYXVkaWVuY2UYBiABKAlCCrpIB3IFEAEYgAQSQAoNcmVwbGF5X3BvbGljeRgHIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYCCABKAVCCrpIBxoFGIQHKAASJQoMd29ya3NwYWNlX2lkGAkgASgJQgq6SAdyBRABGIAESACIAQESIwoKcHJvamVjdF9pZBgKIAEoCUIKukgHcgUQARiABEgBiAEBQg8KDV93b3Jrc3BhY2VfaWRCDQoLX3Byb2plY3RfaWRKBAgEEAVSCnNlc3Npb25faWQijQIKIlN0YXJ0Um9vdFNlc3Npb25Xb3JrQ29udGV4dFJlcXVlc3QSGAoGb3JnX2lkGAEgASgJQgi6SAVyA7ABARIuChlwYXJlbnRfd29ya19jb250ZXh0X3Rva2VuGAIgASgJQgu6SAhyBhABGICAAhIcCgpzZXNzaW9uX2lkGAMgASgJQgi6SAVyA7ABARIcCghhdWRpZW5jZRgEIAEoCUIKukgHcgUQARiABBJACg1yZXBsYXlfcG9saWN5GAUgASgOMikuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFJlcGxheVBvbGljeRIfCgt0dGxfc2Vjb25kcxgGIAEoBUIKukgHGgUYhAcoACK6AgoiRXhjaGFuZ2VXb3JrQ29udGV4dEF1ZGllbmNlUmVxdWVzdBIYCgZvcmdfaWQYASABKAlCCLpIBXIDsAEBEi4KGXBhcmVudF93b3JrX2NvbnRleHRfdG9rZW4YAiABKAlCC7pICHIGEAEYgIACEhwKCGF1ZGllbmNlGAMgASgJQgq6SAdyBRABGIAEEkkKEWF0dGVudWF0ZWRfc2NvcGVzGAQgAygLMiIuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFNjb3BlQgq6SAeSAQQIARBAEkAKDXJlcGxheV9wb2xpY3kYBSABKA4yKS5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0UmVwbGF5UG9saWN5Eh8KC3R0bF9zZWNvbmRzGAYgASgFQgq6SAcaBRiEBygAIvACCiNTdGFydENoaWxkU2Vzc2lvbldvcmtDb250ZXh0UmVxdWVzdBIYCgZvcmdfaWQYASABKAlCCLpIBXIDsAEBEi4KGXBhcmVudF93b3JrX2NvbnRleHRfdG9rZW4YAiABKAlCC7pICHIGEAEYgIACEiQKEmFjdG9yX3ByaW5jaXBhbF9pZBgEIAEoCUIIukgFcgOwAQESRgoOZ3JhbnRlZF9zY29wZXMYBSADKAsyIi5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0U2NvcGVCCrpIB5IBBAgBEEASHAoIYXVkaWVuY2UYBiABKAlCCrpIB3IFEAEYgAQSQAoNcmVwbGF5X3BvbGljeRgHIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYCCABKAVCCrpIBxoFGIQHKABKBAgDEARSCnNlc3Npb25faWQivwIKF1JlbmV3V29ya0NvbnRleHRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESLgoZcGFyZW50X3dvcmtfY29udGV4dF90b2tlbhgCIAEoCUILukgIcgYQARiAgAISIQoIYXVkaWVuY2UYAyABKAlCCrpIB3IFEAEYgARIAIgBARJHChFhdHRlbnVhdGVkX3Njb3BlcxgEIAMoCzIiLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRTY29wZUIIukgFkgECEEASQAoNcmVwbGF5X3BvbGljeRgFIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYBiABKAVCCrpIBxoFGIQHKABCCwoJX2F1ZGllbmNlIvECChFJc3N1ZWRXb3JrQ29udGV4dBINCgV0b2tlbhgBIAEoCRIOCgZvcmdfaWQYAiABKAkSGgoSb3duZXJfcHJpbmNpcGFsX2lkGAMgASgJEg8KB3Rhc2tfaWQYBCABKAkSEgoKc2Vzc2lvbl9pZBgFIAEoCRIeChFwYXJlbnRfc2Vzc2lvbl9pZBgGIAEoCUgAiAEBEiIKGmN1cnJlbnRfYWN0b3JfcHJpbmNpcGFsX2lkGAcgASgJEh4KFmF1dGhvcml6YXRpb25fcmV2aXNpb24YCCABKAQSLgoKZXhwaXJlc19hdBgJIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASGQoMd29ya3NwYWNlX2lkGAogASgJSAGIAQESFwoKcHJvamVjdF9pZBgLIAEoCUgCiAEBQhQKEl9wYXJlbnRfc2Vzc2lvbl9pZEIPCg1fd29ya3NwYWNlX2lkQg0KC19wcm9qZWN0X2lkInoKGldvcmtDb250ZXh0UmV2aXNpb25TdWJqZWN0Eh4KDHByaW5jaXBhbF9pZBgBIAEoCUIIukgFcgOwAQESPAoGc2NvcGVzGAIgAygLMiIuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFNjb3BlQgi6SAWSAQIQQCLUAgohQ2hlY2tBdXRob3JpemF0aW9uUmV2aXNpb25SZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESJAoSb3duZXJfcHJpbmNpcGFsX2lkGAIgASgJQgi6SAVyA7ABARInChZhdXRob3JpemF0aW9uX3JldmlzaW9uGAMgASgEQge6SAQyAiAAEkoKCHN1YmplY3RzGAQgAygLMiwuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFJldmlzaW9uU3ViamVjdEIKukgHkgEECAEQERItChRvcGVyYXRpb25fYmluZGluZ19pZBgFIAEoCUIKukgHcgUQARiABEgAiAEBEh0KEHNlYWxzX25vX2JpbmRpbmcYBiABKAhIAYgBAUIXChVfb3BlcmF0aW9uX2JpbmRpbmdfaWRCEwoRX3NlYWxzX25vX2JpbmRpbmcigwIKHEF1dGhvcml6ZUV2aWRlbmNlUmVhZFJlcXVlc3QSGAoGb3JnX2lkGAEgASgJQgi6SAVyA7ABARIlChNjYWxsZXJfcHJpbmNpcGFsX2lkGAIgASgJQgi6SAVyA7ABARIpChJvd25lcl9wcmluY2lwYWxfaWQYAyABKAlCCLpIBXIDsAEBSACIAQESIAoHdGFza19pZBgEIAEoCUIKukgHcgUQARiABEgBiAEBEiMKCnNlc3Npb25faWQYBSABKAlCCrpIB3IFEAEYgARIAogBAUIVChNfb3duZXJfcHJpbmNpcGFsX2lkQgoKCF90YXNrX2lkQg0KC19zZXNzaW9uX2lkIpYBCiJDb25zdW1lU2luZ2xlVXNlV29ya0NvbnRleHRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESHgoKY29udGV4dF9pZBgCIAEoCUIKukgHcgUQARiABBI2CgpleHBpcmVzX2F0GAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEIGukgDyAEBKpsBChdXb3JrQ29udGV4dFJlcGxheVBvbGljeRIqCiZXT1JLX0NPTlRFWFRfUkVQTEFZX1BPTElDWV9VTlNQRUNJRklFRBAAEikKJVdPUktfQ09OVEVYVF9SRVBMQVlfUE9MSUNZX0lERU1QT1RFTlQQARIpCiVXT1JLX0NPTlRFWFRfUkVQTEFZX1BPTElDWV9TSU5HTEVfVVNFEAIy+wwKEldvcmtDb250ZXh0U2VydmljZRKDAQoaQ2hlY2tBdXRob3JpemF0aW9uUmV2aXNpb24SMy5zYWFzLmFjY291bnRzLnYxLkNoZWNrQXV0aG9yaXphdGlvblJldmlzaW9uUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIYwvMYFAgDEAEwAToCEAFAAUgHUANYA2ABEnkKFUF1dGhvcml6ZUV2aWRlbmNlUmVhZBIuLnNhYXMuYWNjb3VudHMudjEuQXV0aG9yaXplRXZpZGVuY2VSZWFkUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIYwvMYFAgDEAEwAToCEAFAAUgHUANYA2ABEnoKEENvbnN1bWVTaW5nbGVVc2USNC5zYWFzLmFjY291bnRzLnYxLkNvbnN1bWVTaW5nbGVVc2VXb3JrQ29udGV4dFJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiGMLzGBQIAxABMAE6AhABQAFIB1ADWANgARLKAQoJU3RhcnRUYXNrEi0uc2Fhcy5hY2NvdW50cy52MS5TdGFydFRhc2tXb3JrQ29udGV4dFJlcXVlc3QaIy5zYWFzLmFjY291bnRzLnYxLklzc3VlZFdvcmtDb250ZXh0ImnC8xhECAIQAyoMCgZvcmdfaWQQAhgBMAE6Igoec2Fhcy53b3JrX2NvbnRleHQudGFza19zdGFydGVkEAJAAUgFUANYBGABeAKC0+STAhs6ASoiFi92MS93b3JrLWNvbnRleHRzOnRhc2sShgEKFVN0YXJ0SW5zdGFsbGF0aW9uVGFzaxIuLnNhYXMuYWNjb3VudHMudjEuU3RhcnRJbnN0YWxsYXRpb25UYXNrUmVxdWVzdBojLnNhYXMuYWNjb3VudHMudjEuSXNzdWVkV29ya0NvbnRleHQiGMLzGBQIAxABMAE6AhABQAFIB1ADWARgARLoAQoQU3RhcnRSb290U2Vzc2lvbhI0LnNhYXMuYWNjb3VudHMudjEuU3RhcnRSb290U2Vzc2lvbldvcmtDb250ZXh0UmVxdWVzdBojLnNhYXMuYWNjb3VudHMudjEuSXNzdWVkV29ya0NvbnRleHQiecLzGEwIAhADKgwKBm9yZ19pZBACGAEwAToqCiZzYWFzLndvcmtfY29udGV4dC5yb290X3Nlc3Npb25fc3RhcnRlZBACQAFIBVAEWARgAXgCgtPkkwIjOgEqIh4vdjEvd29yay1jb250ZXh0czpyb290LXNlc3Npb24S6wEKEEV4Y2hhbmdlQXVkaWVuY2USNC5zYWFzLmFjY291bnRzLnYxLkV4Y2hhbmdlV29ya0NvbnRleHRBdWRpZW5jZVJlcXVlc3QaIy5zYWFzLmFjY291bnRzLnYxLklzc3VlZFdvcmtDb250ZXh0InzC8xhKCAIQAyoMCgZvcmdfaWQQAhgBMAE6KAokc2Fhcy53b3JrX2NvbnRleHQuYXVkaWVuY2VfZXhjaGFuZ2VkEAJAAUgFUARYBGABeAKC0+STAig6ASoiIy92MS93b3JrLWNvbnRleHRzOmV4Y2hhbmdlLWF1ZGllbmNlEuwBChFTdGFydENoaWxkU2Vzc2lvbhI1LnNhYXMuYWNjb3VudHMudjEuU3RhcnRDaGlsZFNlc3Npb25Xb3JrQ29udGV4dFJlcXVlc3QaIy5zYWFzLmFjY291bnRzLnYxLklzc3VlZFdvcmtDb250ZXh0InvC8xhNCAIQAyoMCgZvcmdfaWQQAhgBMAE6Kwonc2Fhcy53b3JrX2NvbnRleHQuY2hpbGRfc2Vzc2lvbl9zdGFydGVkEAJAAUgFUARYBGABeAKC0+STAiQ6ASoiHy92MS93b3JrLWNvbnRleHRzOmNoaWxkLXNlc3Npb24SyQEKEFJlbmV3V29ya0NvbnRleHQSKS5zYWFzLmFjY291bnRzLnYxLlJlbmV3V29ya0NvbnRleHRSZXF1ZXN0GiMuc2Fhcy5hY2NvdW50cy52MS5Jc3N1ZWRXb3JrQ29udGV4dCJlwvMYPwgCEAMqDAoGb3JnX2lkEAIYATABOh0KGXNhYXMud29ya19jb250ZXh0LnJlbmV3ZWQQAkABSAVQBFgEYAF4AoLT5JMCHDoBKiIXL3YxL3dvcmstY29udGV4dHM6cmVuZXdCuQEKFGNvbS5zYWFzLmFjY291bnRzLnYxQhFXb3JrQ29udGV4dHNQcm90b1ABWixhY2NvdW50cy9wa2cvZ2VuL3NhYXMvYWNjb3VudHMvdjE7YWNjb3VudHN2MaICA1NBWKoCEFNhYXMuQWNjb3VudHMuVjHKAhBTYWFzXEFjY291bnRzXFYx4gIcU2Fhc1xBY2NvdW50c1xWMVxHUEJNZXRhZGF0YeoCElNhYXM6OkFjY291bnRzOjpWMWIGcHJvdG8z", [file_buf_validate_validate, file_google_api_annotations, file_google_protobuf_empty, file_google_protobuf_timestamp, file_saas_policy_v1_options]); + fileDesc("CiRzYWFzL2FjY291bnRzL3YxL3dvcmtfY29udGV4dHMucHJvdG8SEHNhYXMuYWNjb3VudHMudjEiiQEKEFdvcmtDb250ZXh0U2NvcGUSIQoNcmVzb3VyY2Vfa2luZBgBIAEoCUIKukgHcgUQARiAARImCgdhY3Rpb25zGAIgAygJQhW6SBKSAQ8IARBAGAEiB3IFEAEYgAESKgoMcmVzb3VyY2VfaWRzGAMgAygJQhS6SBGSAQ4QgAIYASIHcgUQARiABCLrAwobU3RhcnRUYXNrV29ya0NvbnRleHRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESHAoHdGFza19pZBgCIAEoCUILukgI2AEBcgOwAQESGgoSYWN0b3JfcHJpbmNpcGFsX2lkGAQgASgJEkgKEGF1dGhvcml0eV9zY29wZXMYBSADKAsyIi5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0U2NvcGVCCrpIB5IBBAgBEEASHAoIYXVkaWVuY2UYBiABKAlCCrpIB3IFEAEYgAQSQAoNcmVwbGF5X3BvbGljeRgHIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYCCABKAVCCrpIBxoFGIQHKAASJQoMd29ya3NwYWNlX2lkGAkgASgJQgq6SAdyBRABGIAESACIAQESIwoKcHJvamVjdF9pZBgKIAEoCUIKukgHcgUQARiABEgBiAEBEi8KEm9wZXJhdGlvbl9iaW5kaW5ncxgLIAMoCUITukgQkgENEEAYASIHcgUQARiAAkIPCg1fd29ya3NwYWNlX2lkQg0KC19wcm9qZWN0X2lkSgQIAxAEUgpzZXNzaW9uX2lkIr8DChxTdGFydEluc3RhbGxhdGlvblRhc2tSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESIQoPaW5zdGFsbGF0aW9uX2lkGAIgASgJQgi6SAVyA7ABARIZCgd0YXNrX2lkGAMgASgJQgi6SAVyA7ABARJIChBhdXRob3JpdHlfc2NvcGVzGAUgAygLMiIuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFNjb3BlQgq6SAeSAQQIARBAEhwKCGF1ZGllbmNlGAYgASgJQgq6SAdyBRABGIAEEkAKDXJlcGxheV9wb2xpY3kYByABKA4yKS5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0UmVwbGF5UG9saWN5Eh8KC3R0bF9zZWNvbmRzGAggASgFQgq6SAcaBRiEBygAEiUKDHdvcmtzcGFjZV9pZBgJIAEoCUIKukgHcgUQARiABEgAiAEBEiMKCnByb2plY3RfaWQYCiABKAlCCrpIB3IFEAEYgARIAYgBAUIPCg1fd29ya3NwYWNlX2lkQg0KC19wcm9qZWN0X2lkSgQIBBAFUgpzZXNzaW9uX2lkIo0CCiJTdGFydFJvb3RTZXNzaW9uV29ya0NvbnRleHRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESLgoZcGFyZW50X3dvcmtfY29udGV4dF90b2tlbhgCIAEoCUILukgIcgYQARiAgAISHAoKc2Vzc2lvbl9pZBgDIAEoCUIIukgFcgOwAQESHAoIYXVkaWVuY2UYBCABKAlCCrpIB3IFEAEYgAQSQAoNcmVwbGF5X3BvbGljeRgFIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYBiABKAVCCrpIBxoFGIQHKAAiugIKIkV4Y2hhbmdlV29ya0NvbnRleHRBdWRpZW5jZVJlcXVlc3QSGAoGb3JnX2lkGAEgASgJQgi6SAVyA7ABARIuChlwYXJlbnRfd29ya19jb250ZXh0X3Rva2VuGAIgASgJQgu6SAhyBhABGICAAhIcCghhdWRpZW5jZRgDIAEoCUIKukgHcgUQARiABBJJChFhdHRlbnVhdGVkX3Njb3BlcxgEIAMoCzIiLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRTY29wZUIKukgHkgEECAEQQBJACg1yZXBsYXlfcG9saWN5GAUgASgOMikuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFJlcGxheVBvbGljeRIfCgt0dGxfc2Vjb25kcxgGIAEoBUIKukgHGgUYhAcoACLwAgojU3RhcnRDaGlsZFNlc3Npb25Xb3JrQ29udGV4dFJlcXVlc3QSGAoGb3JnX2lkGAEgASgJQgi6SAVyA7ABARIuChlwYXJlbnRfd29ya19jb250ZXh0X3Rva2VuGAIgASgJQgu6SAhyBhABGICAAhIkChJhY3Rvcl9wcmluY2lwYWxfaWQYBCABKAlCCLpIBXIDsAEBEkYKDmdyYW50ZWRfc2NvcGVzGAUgAygLMiIuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFNjb3BlQgq6SAeSAQQIARBAEhwKCGF1ZGllbmNlGAYgASgJQgq6SAdyBRABGIAEEkAKDXJlcGxheV9wb2xpY3kYByABKA4yKS5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0UmVwbGF5UG9saWN5Eh8KC3R0bF9zZWNvbmRzGAggASgFQgq6SAcaBRiEBygASgQIAxAEUgpzZXNzaW9uX2lkIr8CChdSZW5ld1dvcmtDb250ZXh0UmVxdWVzdBIYCgZvcmdfaWQYASABKAlCCLpIBXIDsAEBEi4KGXBhcmVudF93b3JrX2NvbnRleHRfdG9rZW4YAiABKAlCC7pICHIGEAEYgIACEiEKCGF1ZGllbmNlGAMgASgJQgq6SAdyBRABGIAESACIAQESRwoRYXR0ZW51YXRlZF9zY29wZXMYBCADKAsyIi5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0U2NvcGVCCLpIBZIBAhBAEkAKDXJlcGxheV9wb2xpY3kYBSABKA4yKS5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0UmVwbGF5UG9saWN5Eh8KC3R0bF9zZWNvbmRzGAYgASgFQgq6SAcaBRiEBygAQgsKCV9hdWRpZW5jZSLxAgoRSXNzdWVkV29ya0NvbnRleHQSDQoFdG9rZW4YASABKAkSDgoGb3JnX2lkGAIgASgJEhoKEm93bmVyX3ByaW5jaXBhbF9pZBgDIAEoCRIPCgd0YXNrX2lkGAQgASgJEhIKCnNlc3Npb25faWQYBSABKAkSHgoRcGFyZW50X3Nlc3Npb25faWQYBiABKAlIAIgBARIiChpjdXJyZW50X2FjdG9yX3ByaW5jaXBhbF9pZBgHIAEoCRIeChZhdXRob3JpemF0aW9uX3JldmlzaW9uGAggASgEEi4KCmV4cGlyZXNfYXQYCSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhkKDHdvcmtzcGFjZV9pZBgKIAEoCUgBiAEBEhcKCnByb2plY3RfaWQYCyABKAlIAogBAUIUChJfcGFyZW50X3Nlc3Npb25faWRCDwoNX3dvcmtzcGFjZV9pZEINCgtfcHJvamVjdF9pZCJ6ChpXb3JrQ29udGV4dFJldmlzaW9uU3ViamVjdBIeCgxwcmluY2lwYWxfaWQYASABKAlCCLpIBXIDsAEBEjwKBnNjb3BlcxgCIAMoCzIiLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRTY29wZUIIukgFkgECEEAi1AIKIUNoZWNrQXV0aG9yaXphdGlvblJldmlzaW9uUmVxdWVzdBIYCgZvcmdfaWQYASABKAlCCLpIBXIDsAEBEiQKEm93bmVyX3ByaW5jaXBhbF9pZBgCIAEoCUIIukgFcgOwAQESJwoWYXV0aG9yaXphdGlvbl9yZXZpc2lvbhgDIAEoBEIHukgEMgIgABJKCghzdWJqZWN0cxgEIAMoCzIsLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXZpc2lvblN1YmplY3RCCrpIB5IBBAgBEBESLQoUb3BlcmF0aW9uX2JpbmRpbmdfaWQYBSABKAlCCrpIB3IFEAEYgARIAIgBARIdChBzZWFsc19ub19iaW5kaW5nGAYgASgISAGIAQFCFwoVX29wZXJhdGlvbl9iaW5kaW5nX2lkQhMKEV9zZWFsc19ub19iaW5kaW5nIoMCChxBdXRob3JpemVFdmlkZW5jZVJlYWRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESJQoTY2FsbGVyX3ByaW5jaXBhbF9pZBgCIAEoCUIIukgFcgOwAQESKQoSb3duZXJfcHJpbmNpcGFsX2lkGAMgASgJQgi6SAVyA7ABAUgAiAEBEiAKB3Rhc2tfaWQYBCABKAlCCrpIB3IFEAEYgARIAYgBARIjCgpzZXNzaW9uX2lkGAUgASgJQgq6SAdyBRABGIAESAKIAQFCFQoTX293bmVyX3ByaW5jaXBhbF9pZEIKCghfdGFza19pZEINCgtfc2Vzc2lvbl9pZCKWAQoiQ29uc3VtZVNpbmdsZVVzZVdvcmtDb250ZXh0UmVxdWVzdBIYCgZvcmdfaWQYASABKAlCCLpIBXIDsAEBEh4KCmNvbnRleHRfaWQYAiABKAlCCrpIB3IFEAEYgAQSNgoKZXhwaXJlc19hdBgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCBrpIA8gBASqbAQoXV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSKgomV09SS19DT05URVhUX1JFUExBWV9QT0xJQ1lfVU5TUEVDSUZJRUQQABIpCiVXT1JLX0NPTlRFWFRfUkVQTEFZX1BPTElDWV9JREVNUE9URU5UEAESKQolV09SS19DT05URVhUX1JFUExBWV9QT0xJQ1lfU0lOR0xFX1VTRRACMvsMChJXb3JrQ29udGV4dFNlcnZpY2USgwEKGkNoZWNrQXV0aG9yaXphdGlvblJldmlzaW9uEjMuc2Fhcy5hY2NvdW50cy52MS5DaGVja0F1dGhvcml6YXRpb25SZXZpc2lvblJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiGMLzGBQIAxABMAE6AhABQAFIB1ADWANgARJ5ChVBdXRob3JpemVFdmlkZW5jZVJlYWQSLi5zYWFzLmFjY291bnRzLnYxLkF1dGhvcml6ZUV2aWRlbmNlUmVhZFJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiGMLzGBQIAxABMAE6AhABQAFIB1ADWANgARJ6ChBDb25zdW1lU2luZ2xlVXNlEjQuc2Fhcy5hY2NvdW50cy52MS5Db25zdW1lU2luZ2xlVXNlV29ya0NvbnRleHRSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IhjC8xgUCAMQATABOgIQAUABSAdQA1gDYAESygEKCVN0YXJ0VGFzaxItLnNhYXMuYWNjb3VudHMudjEuU3RhcnRUYXNrV29ya0NvbnRleHRSZXF1ZXN0GiMuc2Fhcy5hY2NvdW50cy52MS5Jc3N1ZWRXb3JrQ29udGV4dCJpwvMYRAgCEAMqDAoGb3JnX2lkEAIYATABOiIKHnNhYXMud29ya19jb250ZXh0LnRhc2tfc3RhcnRlZBACQAFIBVADWARgAXgCgtPkkwIbOgEqIhYvdjEvd29yay1jb250ZXh0czp0YXNrEoYBChVTdGFydEluc3RhbGxhdGlvblRhc2sSLi5zYWFzLmFjY291bnRzLnYxLlN0YXJ0SW5zdGFsbGF0aW9uVGFza1JlcXVlc3QaIy5zYWFzLmFjY291bnRzLnYxLklzc3VlZFdvcmtDb250ZXh0IhjC8xgUCAMQATABOgIQAUABSAdQA1gEYAES6AEKEFN0YXJ0Um9vdFNlc3Npb24SNC5zYWFzLmFjY291bnRzLnYxLlN0YXJ0Um9vdFNlc3Npb25Xb3JrQ29udGV4dFJlcXVlc3QaIy5zYWFzLmFjY291bnRzLnYxLklzc3VlZFdvcmtDb250ZXh0InnC8xhMCAIQAyoMCgZvcmdfaWQQAhgBMAE6Kgomc2Fhcy53b3JrX2NvbnRleHQucm9vdF9zZXNzaW9uX3N0YXJ0ZWQQAkABSAVQBFgEYAF4AoLT5JMCIzoBKiIeL3YxL3dvcmstY29udGV4dHM6cm9vdC1zZXNzaW9uEusBChBFeGNoYW5nZUF1ZGllbmNlEjQuc2Fhcy5hY2NvdW50cy52MS5FeGNoYW5nZVdvcmtDb250ZXh0QXVkaWVuY2VSZXF1ZXN0GiMuc2Fhcy5hY2NvdW50cy52MS5Jc3N1ZWRXb3JrQ29udGV4dCJ8wvMYSggCEAMqDAoGb3JnX2lkEAIYATABOigKJHNhYXMud29ya19jb250ZXh0LmF1ZGllbmNlX2V4Y2hhbmdlZBACQAFIBVAEWARgAXgCgtPkkwIoOgEqIiMvdjEvd29yay1jb250ZXh0czpleGNoYW5nZS1hdWRpZW5jZRLsAQoRU3RhcnRDaGlsZFNlc3Npb24SNS5zYWFzLmFjY291bnRzLnYxLlN0YXJ0Q2hpbGRTZXNzaW9uV29ya0NvbnRleHRSZXF1ZXN0GiMuc2Fhcy5hY2NvdW50cy52MS5Jc3N1ZWRXb3JrQ29udGV4dCJ7wvMYTQgCEAMqDAoGb3JnX2lkEAIYATABOisKJ3NhYXMud29ya19jb250ZXh0LmNoaWxkX3Nlc3Npb25fc3RhcnRlZBACQAFIBVAEWARgAXgCgtPkkwIkOgEqIh8vdjEvd29yay1jb250ZXh0czpjaGlsZC1zZXNzaW9uEskBChBSZW5ld1dvcmtDb250ZXh0Eikuc2Fhcy5hY2NvdW50cy52MS5SZW5ld1dvcmtDb250ZXh0UmVxdWVzdBojLnNhYXMuYWNjb3VudHMudjEuSXNzdWVkV29ya0NvbnRleHQiZcLzGD8IAhADKgwKBm9yZ19pZBACGAEwATodChlzYWFzLndvcmtfY29udGV4dC5yZW5ld2VkEAJAAUgFUARYBGABeAKC0+STAhw6ASoiFy92MS93b3JrLWNvbnRleHRzOnJlbmV3QrkBChRjb20uc2Fhcy5hY2NvdW50cy52MUIRV29ya0NvbnRleHRzUHJvdG9QAVosYWNjb3VudHMvcGtnL2dlbi9zYWFzL2FjY291bnRzL3YxO2FjY291bnRzdjGiAgNTQViqAhBTYWFzLkFjY291bnRzLlYxygIQU2Fhc1xBY2NvdW50c1xWMeICHFNhYXNcQWNjb3VudHNcVjFcR1BCTWV0YWRhdGHqAhJTYWFzOjpBY2NvdW50czo6VjFiBnByb3RvMw", [file_buf_validate_validate, file_google_api_annotations, file_google_protobuf_empty, file_google_protobuf_timestamp, file_saas_policy_v1_options]); /** * WorkContextScope is one product-neutral authority slice. Product plugins @@ -123,6 +123,18 @@ export type StartTaskWorkContextRequest = Message<"saas.accounts.v1.StartTaskWor * @generated from field: optional string project_id = 10; */ projectId?: string; + + /** + * Operation bindings of the audience module, by name, whose invoke scopes + * this capability carries beside authority_scopes. The host resolves each + * name to the binding as installed at org_id, narrowed by that installation + * exactly as the module's delegated exchange redeems it, and keeps each + * resource only if the person holds it. The caller names a binding and never + * its resource ids. A direct human-owned mint only. + * + * @generated from field: repeated string operation_bindings = 11; + */ + operationBindings: string[]; }; /** From 8d83e9d6eba35b4652019e3fb1c4860f36286cb8 Mon Sep 17 00:00:00 2001 From: Luka Zhupa <5446951+lukazhupa@users.noreply.github.com> Date: Thu, 22 Oct 2026 16:02:35 -0400 Subject: [PATCH 2/2] chore(saas-sdk): regenerate the library from the accounts/connect contract npm run generate -- --force with codefly 0.1.185, so library.codefly.yaml records the contract digest the package now publishes. --- .../generated/contract/catalog.codefly.json | 2 +- .../generated/contract/contract.binpb | Bin 1199552 -> 1200272 bytes .../saas-sdk/generated/library.codefly.yaml | 4 ++-- .../gen/saas/accounts/v1/work_contexts_pb.ts | 14 +++++++++++++- 4 files changed, 16 insertions(+), 4 deletions(-) diff --git a/module/services/frontend/code/packages/saas-sdk/generated/contract/catalog.codefly.json b/module/services/frontend/code/packages/saas-sdk/generated/contract/catalog.codefly.json index 363d105db..acf77f39d 100644 --- a/module/services/frontend/code/packages/saas-sdk/generated/contract/catalog.codefly.json +++ b/module/services/frontend/code/packages/saas-sdk/generated/contract/catalog.codefly.json @@ -10,7 +10,7 @@ "kind": "protobuf", "package": "saas.accounts.v1", "path": "contract.binpb", - "digest": "sha256:373a8f3a22b7968c36dd9faffb9be5ef64213b1de2a532f5e567007bd65caaa7", + "digest": "sha256:21f848eb59ce4aca4903d354b66131c8e9595771d505d762fa07e730f32c4c4c", "services": [ { "name": "APIKeyService", diff --git a/module/services/frontend/code/packages/saas-sdk/generated/contract/contract.binpb b/module/services/frontend/code/packages/saas-sdk/generated/contract/contract.binpb index 590c0a8eb1d2d00c9520361083a0d0e6c244e78f..9e7aba42afcbad452c7a94ab77c66ba54c249e49 100644 GIT binary patch delta 10295 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 JX*9Ox{{ay{G|2z} delta 9561 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 P0RIM7$A2;Ye%=2Ai9&AS diff --git a/module/services/frontend/code/packages/saas-sdk/generated/library.codefly.yaml b/module/services/frontend/code/packages/saas-sdk/generated/library.codefly.yaml index e907bd78a..e4ae5d3e4 100644 --- a/module/services/frontend/code/packages/saas-sdk/generated/library.codefly.yaml +++ b/module/services/frontend/code/packages/saas-sdk/generated/library.codefly.yaml @@ -12,7 +12,7 @@ sources: module: saas-starter service: accounts endpoint: connect - contract-digest: sha256:373a8f3a22b7968c36dd9faffb9be5ef64213b1de2a532f5e567007bd65caaa7 + contract-digest: sha256:21f848eb59ce4aca4903d354b66131c8e9595771d505d762fa07e730f32c4c4c services: - APIKeyService - AccessibleScopeService @@ -52,6 +52,6 @@ sources: - WorkContextService as: accounts generated-by: - codefly: 0.1.191 + codefly: 0.1.185 companion: ghcr.io/codefly-dev/proto:0.0.16 facade: true diff --git a/module/services/frontend/code/packages/saas-sdk/generated/typescript/src/gen/saas/accounts/v1/work_contexts_pb.ts b/module/services/frontend/code/packages/saas-sdk/generated/typescript/src/gen/saas/accounts/v1/work_contexts_pb.ts index 94cbce45c..0d7e2a687 100644 --- a/module/services/frontend/code/packages/saas-sdk/generated/typescript/src/gen/saas/accounts/v1/work_contexts_pb.ts +++ b/module/services/frontend/code/packages/saas-sdk/generated/typescript/src/gen/saas/accounts/v1/work_contexts_pb.ts @@ -15,7 +15,7 @@ import type { Message } from "@bufbuild/protobuf"; * Describes the file saas/accounts/v1/work_contexts.proto. */ export const file_saas_accounts_v1_work_contexts: GenFile = /*@__PURE__*/ - fileDesc("CiRzYWFzL2FjY291bnRzL3YxL3dvcmtfY29udGV4dHMucHJvdG8SEHNhYXMuYWNjb3VudHMudjEiiQEKEFdvcmtDb250ZXh0U2NvcGUSIQoNcmVzb3VyY2Vfa2luZBgBIAEoCUIKukgHcgUQARiAARImCgdhY3Rpb25zGAIgAygJQhW6SBKSAQ8IARBAGAEiB3IFEAEYgAESKgoMcmVzb3VyY2VfaWRzGAMgAygJQhS6SBGSAQ4QgAIYASIHcgUQARiABCK6AwobU3RhcnRUYXNrV29ya0NvbnRleHRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESHAoHdGFza19pZBgCIAEoCUILukgI2AEBcgOwAQESGgoSYWN0b3JfcHJpbmNpcGFsX2lkGAQgASgJEkgKEGF1dGhvcml0eV9zY29wZXMYBSADKAsyIi5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0U2NvcGVCCrpIB5IBBAgBEEASHAoIYXVkaWVuY2UYBiABKAlCCrpIB3IFEAEYgAQSQAoNcmVwbGF5X3BvbGljeRgHIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYCCABKAVCCrpIBxoFGIQHKAASJQoMd29ya3NwYWNlX2lkGAkgASgJQgq6SAdyBRABGIAESACIAQESIwoKcHJvamVjdF9pZBgKIAEoCUIKukgHcgUQARiABEgBiAEBQg8KDV93b3Jrc3BhY2VfaWRCDQoLX3Byb2plY3RfaWRKBAgDEARSCnNlc3Npb25faWQivwMKHFN0YXJ0SW5zdGFsbGF0aW9uVGFza1JlcXVlc3QSGAoGb3JnX2lkGAEgASgJQgi6SAVyA7ABARIhCg9pbnN0YWxsYXRpb25faWQYAiABKAlCCLpIBXIDsAEBEhkKB3Rhc2tfaWQYAyABKAlCCLpIBXIDsAEBEkgKEGF1dGhvcml0eV9zY29wZXMYBSADKAsyIi5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0U2NvcGVCCrpIB5IBBAgBEEASHAoIYXVkaWVuY2UYBiABKAlCCrpIB3IFEAEYgAQSQAoNcmVwbGF5X3BvbGljeRgHIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYCCABKAVCCrpIBxoFGIQHKAASJQoMd29ya3NwYWNlX2lkGAkgASgJQgq6SAdyBRABGIAESACIAQESIwoKcHJvamVjdF9pZBgKIAEoCUIKukgHcgUQARiABEgBiAEBQg8KDV93b3Jrc3BhY2VfaWRCDQoLX3Byb2plY3RfaWRKBAgEEAVSCnNlc3Npb25faWQijQIKIlN0YXJ0Um9vdFNlc3Npb25Xb3JrQ29udGV4dFJlcXVlc3QSGAoGb3JnX2lkGAEgASgJQgi6SAVyA7ABARIuChlwYXJlbnRfd29ya19jb250ZXh0X3Rva2VuGAIgASgJQgu6SAhyBhABGICAAhIcCgpzZXNzaW9uX2lkGAMgASgJQgi6SAVyA7ABARIcCghhdWRpZW5jZRgEIAEoCUIKukgHcgUQARiABBJACg1yZXBsYXlfcG9saWN5GAUgASgOMikuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFJlcGxheVBvbGljeRIfCgt0dGxfc2Vjb25kcxgGIAEoBUIKukgHGgUYhAcoACK6AgoiRXhjaGFuZ2VXb3JrQ29udGV4dEF1ZGllbmNlUmVxdWVzdBIYCgZvcmdfaWQYASABKAlCCLpIBXIDsAEBEi4KGXBhcmVudF93b3JrX2NvbnRleHRfdG9rZW4YAiABKAlCC7pICHIGEAEYgIACEhwKCGF1ZGllbmNlGAMgASgJQgq6SAdyBRABGIAEEkkKEWF0dGVudWF0ZWRfc2NvcGVzGAQgAygLMiIuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFNjb3BlQgq6SAeSAQQIARBAEkAKDXJlcGxheV9wb2xpY3kYBSABKA4yKS5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0UmVwbGF5UG9saWN5Eh8KC3R0bF9zZWNvbmRzGAYgASgFQgq6SAcaBRiEBygAIvACCiNTdGFydENoaWxkU2Vzc2lvbldvcmtDb250ZXh0UmVxdWVzdBIYCgZvcmdfaWQYASABKAlCCLpIBXIDsAEBEi4KGXBhcmVudF93b3JrX2NvbnRleHRfdG9rZW4YAiABKAlCC7pICHIGEAEYgIACEiQKEmFjdG9yX3ByaW5jaXBhbF9pZBgEIAEoCUIIukgFcgOwAQESRgoOZ3JhbnRlZF9zY29wZXMYBSADKAsyIi5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0U2NvcGVCCrpIB5IBBAgBEEASHAoIYXVkaWVuY2UYBiABKAlCCrpIB3IFEAEYgAQSQAoNcmVwbGF5X3BvbGljeRgHIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYCCABKAVCCrpIBxoFGIQHKABKBAgDEARSCnNlc3Npb25faWQivwIKF1JlbmV3V29ya0NvbnRleHRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESLgoZcGFyZW50X3dvcmtfY29udGV4dF90b2tlbhgCIAEoCUILukgIcgYQARiAgAISIQoIYXVkaWVuY2UYAyABKAlCCrpIB3IFEAEYgARIAIgBARJHChFhdHRlbnVhdGVkX3Njb3BlcxgEIAMoCzIiLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRTY29wZUIIukgFkgECEEASQAoNcmVwbGF5X3BvbGljeRgFIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYBiABKAVCCrpIBxoFGIQHKABCCwoJX2F1ZGllbmNlIvECChFJc3N1ZWRXb3JrQ29udGV4dBINCgV0b2tlbhgBIAEoCRIOCgZvcmdfaWQYAiABKAkSGgoSb3duZXJfcHJpbmNpcGFsX2lkGAMgASgJEg8KB3Rhc2tfaWQYBCABKAkSEgoKc2Vzc2lvbl9pZBgFIAEoCRIeChFwYXJlbnRfc2Vzc2lvbl9pZBgGIAEoCUgAiAEBEiIKGmN1cnJlbnRfYWN0b3JfcHJpbmNpcGFsX2lkGAcgASgJEh4KFmF1dGhvcml6YXRpb25fcmV2aXNpb24YCCABKAQSLgoKZXhwaXJlc19hdBgJIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXASGQoMd29ya3NwYWNlX2lkGAogASgJSAGIAQESFwoKcHJvamVjdF9pZBgLIAEoCUgCiAEBQhQKEl9wYXJlbnRfc2Vzc2lvbl9pZEIPCg1fd29ya3NwYWNlX2lkQg0KC19wcm9qZWN0X2lkInoKGldvcmtDb250ZXh0UmV2aXNpb25TdWJqZWN0Eh4KDHByaW5jaXBhbF9pZBgBIAEoCUIIukgFcgOwAQESPAoGc2NvcGVzGAIgAygLMiIuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFNjb3BlQgi6SAWSAQIQQCLUAgohQ2hlY2tBdXRob3JpemF0aW9uUmV2aXNpb25SZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESJAoSb3duZXJfcHJpbmNpcGFsX2lkGAIgASgJQgi6SAVyA7ABARInChZhdXRob3JpemF0aW9uX3JldmlzaW9uGAMgASgEQge6SAQyAiAAEkoKCHN1YmplY3RzGAQgAygLMiwuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFJldmlzaW9uU3ViamVjdEIKukgHkgEECAEQERItChRvcGVyYXRpb25fYmluZGluZ19pZBgFIAEoCUIKukgHcgUQARiABEgAiAEBEh0KEHNlYWxzX25vX2JpbmRpbmcYBiABKAhIAYgBAUIXChVfb3BlcmF0aW9uX2JpbmRpbmdfaWRCEwoRX3NlYWxzX25vX2JpbmRpbmcigwIKHEF1dGhvcml6ZUV2aWRlbmNlUmVhZFJlcXVlc3QSGAoGb3JnX2lkGAEgASgJQgi6SAVyA7ABARIlChNjYWxsZXJfcHJpbmNpcGFsX2lkGAIgASgJQgi6SAVyA7ABARIpChJvd25lcl9wcmluY2lwYWxfaWQYAyABKAlCCLpIBXIDsAEBSACIAQESIAoHdGFza19pZBgEIAEoCUIKukgHcgUQARiABEgBiAEBEiMKCnNlc3Npb25faWQYBSABKAlCCrpIB3IFEAEYgARIAogBAUIVChNfb3duZXJfcHJpbmNpcGFsX2lkQgoKCF90YXNrX2lkQg0KC19zZXNzaW9uX2lkIpYBCiJDb25zdW1lU2luZ2xlVXNlV29ya0NvbnRleHRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESHgoKY29udGV4dF9pZBgCIAEoCUIKukgHcgUQARiABBI2CgpleHBpcmVzX2F0GAMgASgLMhouZ29vZ2xlLnByb3RvYnVmLlRpbWVzdGFtcEIGukgDyAEBKpsBChdXb3JrQ29udGV4dFJlcGxheVBvbGljeRIqCiZXT1JLX0NPTlRFWFRfUkVQTEFZX1BPTElDWV9VTlNQRUNJRklFRBAAEikKJVdPUktfQ09OVEVYVF9SRVBMQVlfUE9MSUNZX0lERU1QT1RFTlQQARIpCiVXT1JLX0NPTlRFWFRfUkVQTEFZX1BPTElDWV9TSU5HTEVfVVNFEAIy+wwKEldvcmtDb250ZXh0U2VydmljZRKDAQoaQ2hlY2tBdXRob3JpemF0aW9uUmV2aXNpb24SMy5zYWFzLmFjY291bnRzLnYxLkNoZWNrQXV0aG9yaXphdGlvblJldmlzaW9uUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIYwvMYFAgDEAEwAToCEAFAAUgHUANYA2ABEnkKFUF1dGhvcml6ZUV2aWRlbmNlUmVhZBIuLnNhYXMuYWNjb3VudHMudjEuQXV0aG9yaXplRXZpZGVuY2VSZWFkUmVxdWVzdBoWLmdvb2dsZS5wcm90b2J1Zi5FbXB0eSIYwvMYFAgDEAEwAToCEAFAAUgHUANYA2ABEnoKEENvbnN1bWVTaW5nbGVVc2USNC5zYWFzLmFjY291bnRzLnYxLkNvbnN1bWVTaW5nbGVVc2VXb3JrQ29udGV4dFJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiGMLzGBQIAxABMAE6AhABQAFIB1ADWANgARLKAQoJU3RhcnRUYXNrEi0uc2Fhcy5hY2NvdW50cy52MS5TdGFydFRhc2tXb3JrQ29udGV4dFJlcXVlc3QaIy5zYWFzLmFjY291bnRzLnYxLklzc3VlZFdvcmtDb250ZXh0ImnC8xhECAIQAyoMCgZvcmdfaWQQAhgBMAE6Igoec2Fhcy53b3JrX2NvbnRleHQudGFza19zdGFydGVkEAJAAUgFUANYBGABeAKC0+STAhs6ASoiFi92MS93b3JrLWNvbnRleHRzOnRhc2sShgEKFVN0YXJ0SW5zdGFsbGF0aW9uVGFzaxIuLnNhYXMuYWNjb3VudHMudjEuU3RhcnRJbnN0YWxsYXRpb25UYXNrUmVxdWVzdBojLnNhYXMuYWNjb3VudHMudjEuSXNzdWVkV29ya0NvbnRleHQiGMLzGBQIAxABMAE6AhABQAFIB1ADWARgARLoAQoQU3RhcnRSb290U2Vzc2lvbhI0LnNhYXMuYWNjb3VudHMudjEuU3RhcnRSb290U2Vzc2lvbldvcmtDb250ZXh0UmVxdWVzdBojLnNhYXMuYWNjb3VudHMudjEuSXNzdWVkV29ya0NvbnRleHQiecLzGEwIAhADKgwKBm9yZ19pZBACGAEwAToqCiZzYWFzLndvcmtfY29udGV4dC5yb290X3Nlc3Npb25fc3RhcnRlZBACQAFIBVAEWARgAXgCgtPkkwIjOgEqIh4vdjEvd29yay1jb250ZXh0czpyb290LXNlc3Npb24S6wEKEEV4Y2hhbmdlQXVkaWVuY2USNC5zYWFzLmFjY291bnRzLnYxLkV4Y2hhbmdlV29ya0NvbnRleHRBdWRpZW5jZVJlcXVlc3QaIy5zYWFzLmFjY291bnRzLnYxLklzc3VlZFdvcmtDb250ZXh0InzC8xhKCAIQAyoMCgZvcmdfaWQQAhgBMAE6KAokc2Fhcy53b3JrX2NvbnRleHQuYXVkaWVuY2VfZXhjaGFuZ2VkEAJAAUgFUARYBGABeAKC0+STAig6ASoiIy92MS93b3JrLWNvbnRleHRzOmV4Y2hhbmdlLWF1ZGllbmNlEuwBChFTdGFydENoaWxkU2Vzc2lvbhI1LnNhYXMuYWNjb3VudHMudjEuU3RhcnRDaGlsZFNlc3Npb25Xb3JrQ29udGV4dFJlcXVlc3QaIy5zYWFzLmFjY291bnRzLnYxLklzc3VlZFdvcmtDb250ZXh0InvC8xhNCAIQAyoMCgZvcmdfaWQQAhgBMAE6Kwonc2Fhcy53b3JrX2NvbnRleHQuY2hpbGRfc2Vzc2lvbl9zdGFydGVkEAJAAUgFUARYBGABeAKC0+STAiQ6ASoiHy92MS93b3JrLWNvbnRleHRzOmNoaWxkLXNlc3Npb24SyQEKEFJlbmV3V29ya0NvbnRleHQSKS5zYWFzLmFjY291bnRzLnYxLlJlbmV3V29ya0NvbnRleHRSZXF1ZXN0GiMuc2Fhcy5hY2NvdW50cy52MS5Jc3N1ZWRXb3JrQ29udGV4dCJlwvMYPwgCEAMqDAoGb3JnX2lkEAIYATABOh0KGXNhYXMud29ya19jb250ZXh0LnJlbmV3ZWQQAkABSAVQBFgEYAF4AoLT5JMCHDoBKiIXL3YxL3dvcmstY29udGV4dHM6cmVuZXdiBnByb3RvMw", [file_buf_validate_validate, file_google_api_annotations, file_google_protobuf_empty, file_google_protobuf_timestamp, file_saas_policy_v1_options]); + fileDesc("CiRzYWFzL2FjY291bnRzL3YxL3dvcmtfY29udGV4dHMucHJvdG8SEHNhYXMuYWNjb3VudHMudjEiiQEKEFdvcmtDb250ZXh0U2NvcGUSIQoNcmVzb3VyY2Vfa2luZBgBIAEoCUIKukgHcgUQARiAARImCgdhY3Rpb25zGAIgAygJQhW6SBKSAQ8IARBAGAEiB3IFEAEYgAESKgoMcmVzb3VyY2VfaWRzGAMgAygJQhS6SBGSAQ4QgAIYASIHcgUQARiABCLrAwobU3RhcnRUYXNrV29ya0NvbnRleHRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESHAoHdGFza19pZBgCIAEoCUILukgI2AEBcgOwAQESGgoSYWN0b3JfcHJpbmNpcGFsX2lkGAQgASgJEkgKEGF1dGhvcml0eV9zY29wZXMYBSADKAsyIi5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0U2NvcGVCCrpIB5IBBAgBEEASHAoIYXVkaWVuY2UYBiABKAlCCrpIB3IFEAEYgAQSQAoNcmVwbGF5X3BvbGljeRgHIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYCCABKAVCCrpIBxoFGIQHKAASJQoMd29ya3NwYWNlX2lkGAkgASgJQgq6SAdyBRABGIAESACIAQESIwoKcHJvamVjdF9pZBgKIAEoCUIKukgHcgUQARiABEgBiAEBEi8KEm9wZXJhdGlvbl9iaW5kaW5ncxgLIAMoCUITukgQkgENEEAYASIHcgUQARiAAkIPCg1fd29ya3NwYWNlX2lkQg0KC19wcm9qZWN0X2lkSgQIAxAEUgpzZXNzaW9uX2lkIr8DChxTdGFydEluc3RhbGxhdGlvblRhc2tSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESIQoPaW5zdGFsbGF0aW9uX2lkGAIgASgJQgi6SAVyA7ABARIZCgd0YXNrX2lkGAMgASgJQgi6SAVyA7ABARJIChBhdXRob3JpdHlfc2NvcGVzGAUgAygLMiIuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFNjb3BlQgq6SAeSAQQIARBAEhwKCGF1ZGllbmNlGAYgASgJQgq6SAdyBRABGIAEEkAKDXJlcGxheV9wb2xpY3kYByABKA4yKS5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0UmVwbGF5UG9saWN5Eh8KC3R0bF9zZWNvbmRzGAggASgFQgq6SAcaBRiEBygAEiUKDHdvcmtzcGFjZV9pZBgJIAEoCUIKukgHcgUQARiABEgAiAEBEiMKCnByb2plY3RfaWQYCiABKAlCCrpIB3IFEAEYgARIAYgBAUIPCg1fd29ya3NwYWNlX2lkQg0KC19wcm9qZWN0X2lkSgQIBBAFUgpzZXNzaW9uX2lkIo0CCiJTdGFydFJvb3RTZXNzaW9uV29ya0NvbnRleHRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESLgoZcGFyZW50X3dvcmtfY29udGV4dF90b2tlbhgCIAEoCUILukgIcgYQARiAgAISHAoKc2Vzc2lvbl9pZBgDIAEoCUIIukgFcgOwAQESHAoIYXVkaWVuY2UYBCABKAlCCrpIB3IFEAEYgAQSQAoNcmVwbGF5X3BvbGljeRgFIAEoDjIpLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSHwoLdHRsX3NlY29uZHMYBiABKAVCCrpIBxoFGIQHKAAiugIKIkV4Y2hhbmdlV29ya0NvbnRleHRBdWRpZW5jZVJlcXVlc3QSGAoGb3JnX2lkGAEgASgJQgi6SAVyA7ABARIuChlwYXJlbnRfd29ya19jb250ZXh0X3Rva2VuGAIgASgJQgu6SAhyBhABGICAAhIcCghhdWRpZW5jZRgDIAEoCUIKukgHcgUQARiABBJJChFhdHRlbnVhdGVkX3Njb3BlcxgEIAMoCzIiLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRTY29wZUIKukgHkgEECAEQQBJACg1yZXBsYXlfcG9saWN5GAUgASgOMikuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFJlcGxheVBvbGljeRIfCgt0dGxfc2Vjb25kcxgGIAEoBUIKukgHGgUYhAcoACLwAgojU3RhcnRDaGlsZFNlc3Npb25Xb3JrQ29udGV4dFJlcXVlc3QSGAoGb3JnX2lkGAEgASgJQgi6SAVyA7ABARIuChlwYXJlbnRfd29ya19jb250ZXh0X3Rva2VuGAIgASgJQgu6SAhyBhABGICAAhIkChJhY3Rvcl9wcmluY2lwYWxfaWQYBCABKAlCCLpIBXIDsAEBEkYKDmdyYW50ZWRfc2NvcGVzGAUgAygLMiIuc2Fhcy5hY2NvdW50cy52MS5Xb3JrQ29udGV4dFNjb3BlQgq6SAeSAQQIARBAEhwKCGF1ZGllbmNlGAYgASgJQgq6SAdyBRABGIAEEkAKDXJlcGxheV9wb2xpY3kYByABKA4yKS5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0UmVwbGF5UG9saWN5Eh8KC3R0bF9zZWNvbmRzGAggASgFQgq6SAcaBRiEBygASgQIAxAEUgpzZXNzaW9uX2lkIr8CChdSZW5ld1dvcmtDb250ZXh0UmVxdWVzdBIYCgZvcmdfaWQYASABKAlCCLpIBXIDsAEBEi4KGXBhcmVudF93b3JrX2NvbnRleHRfdG9rZW4YAiABKAlCC7pICHIGEAEYgIACEiEKCGF1ZGllbmNlGAMgASgJQgq6SAdyBRABGIAESACIAQESRwoRYXR0ZW51YXRlZF9zY29wZXMYBCADKAsyIi5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0U2NvcGVCCLpIBZIBAhBAEkAKDXJlcGxheV9wb2xpY3kYBSABKA4yKS5zYWFzLmFjY291bnRzLnYxLldvcmtDb250ZXh0UmVwbGF5UG9saWN5Eh8KC3R0bF9zZWNvbmRzGAYgASgFQgq6SAcaBRiEBygAQgsKCV9hdWRpZW5jZSLxAgoRSXNzdWVkV29ya0NvbnRleHQSDQoFdG9rZW4YASABKAkSDgoGb3JnX2lkGAIgASgJEhoKEm93bmVyX3ByaW5jaXBhbF9pZBgDIAEoCRIPCgd0YXNrX2lkGAQgASgJEhIKCnNlc3Npb25faWQYBSABKAkSHgoRcGFyZW50X3Nlc3Npb25faWQYBiABKAlIAIgBARIiChpjdXJyZW50X2FjdG9yX3ByaW5jaXBhbF9pZBgHIAEoCRIeChZhdXRob3JpemF0aW9uX3JldmlzaW9uGAggASgEEi4KCmV4cGlyZXNfYXQYCSABKAsyGi5nb29nbGUucHJvdG9idWYuVGltZXN0YW1wEhkKDHdvcmtzcGFjZV9pZBgKIAEoCUgBiAEBEhcKCnByb2plY3RfaWQYCyABKAlIAogBAUIUChJfcGFyZW50X3Nlc3Npb25faWRCDwoNX3dvcmtzcGFjZV9pZEINCgtfcHJvamVjdF9pZCJ6ChpXb3JrQ29udGV4dFJldmlzaW9uU3ViamVjdBIeCgxwcmluY2lwYWxfaWQYASABKAlCCLpIBXIDsAEBEjwKBnNjb3BlcxgCIAMoCzIiLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRTY29wZUIIukgFkgECEEAi1AIKIUNoZWNrQXV0aG9yaXphdGlvblJldmlzaW9uUmVxdWVzdBIYCgZvcmdfaWQYASABKAlCCLpIBXIDsAEBEiQKEm93bmVyX3ByaW5jaXBhbF9pZBgCIAEoCUIIukgFcgOwAQESJwoWYXV0aG9yaXphdGlvbl9yZXZpc2lvbhgDIAEoBEIHukgEMgIgABJKCghzdWJqZWN0cxgEIAMoCzIsLnNhYXMuYWNjb3VudHMudjEuV29ya0NvbnRleHRSZXZpc2lvblN1YmplY3RCCrpIB5IBBAgBEBESLQoUb3BlcmF0aW9uX2JpbmRpbmdfaWQYBSABKAlCCrpIB3IFEAEYgARIAIgBARIdChBzZWFsc19ub19iaW5kaW5nGAYgASgISAGIAQFCFwoVX29wZXJhdGlvbl9iaW5kaW5nX2lkQhMKEV9zZWFsc19ub19iaW5kaW5nIoMCChxBdXRob3JpemVFdmlkZW5jZVJlYWRSZXF1ZXN0EhgKBm9yZ19pZBgBIAEoCUIIukgFcgOwAQESJQoTY2FsbGVyX3ByaW5jaXBhbF9pZBgCIAEoCUIIukgFcgOwAQESKQoSb3duZXJfcHJpbmNpcGFsX2lkGAMgASgJQgi6SAVyA7ABAUgAiAEBEiAKB3Rhc2tfaWQYBCABKAlCCrpIB3IFEAEYgARIAYgBARIjCgpzZXNzaW9uX2lkGAUgASgJQgq6SAdyBRABGIAESAKIAQFCFQoTX293bmVyX3ByaW5jaXBhbF9pZEIKCghfdGFza19pZEINCgtfc2Vzc2lvbl9pZCKWAQoiQ29uc3VtZVNpbmdsZVVzZVdvcmtDb250ZXh0UmVxdWVzdBIYCgZvcmdfaWQYASABKAlCCLpIBXIDsAEBEh4KCmNvbnRleHRfaWQYAiABKAlCCrpIB3IFEAEYgAQSNgoKZXhwaXJlc19hdBgDIAEoCzIaLmdvb2dsZS5wcm90b2J1Zi5UaW1lc3RhbXBCBrpIA8gBASqbAQoXV29ya0NvbnRleHRSZXBsYXlQb2xpY3kSKgomV09SS19DT05URVhUX1JFUExBWV9QT0xJQ1lfVU5TUEVDSUZJRUQQABIpCiVXT1JLX0NPTlRFWFRfUkVQTEFZX1BPTElDWV9JREVNUE9URU5UEAESKQolV09SS19DT05URVhUX1JFUExBWV9QT0xJQ1lfU0lOR0xFX1VTRRACMvsMChJXb3JrQ29udGV4dFNlcnZpY2USgwEKGkNoZWNrQXV0aG9yaXphdGlvblJldmlzaW9uEjMuc2Fhcy5hY2NvdW50cy52MS5DaGVja0F1dGhvcml6YXRpb25SZXZpc2lvblJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiGMLzGBQIAxABMAE6AhABQAFIB1ADWANgARJ5ChVBdXRob3JpemVFdmlkZW5jZVJlYWQSLi5zYWFzLmFjY291bnRzLnYxLkF1dGhvcml6ZUV2aWRlbmNlUmVhZFJlcXVlc3QaFi5nb29nbGUucHJvdG9idWYuRW1wdHkiGMLzGBQIAxABMAE6AhABQAFIB1ADWANgARJ6ChBDb25zdW1lU2luZ2xlVXNlEjQuc2Fhcy5hY2NvdW50cy52MS5Db25zdW1lU2luZ2xlVXNlV29ya0NvbnRleHRSZXF1ZXN0GhYuZ29vZ2xlLnByb3RvYnVmLkVtcHR5IhjC8xgUCAMQATABOgIQAUABSAdQA1gDYAESygEKCVN0YXJ0VGFzaxItLnNhYXMuYWNjb3VudHMudjEuU3RhcnRUYXNrV29ya0NvbnRleHRSZXF1ZXN0GiMuc2Fhcy5hY2NvdW50cy52MS5Jc3N1ZWRXb3JrQ29udGV4dCJpwvMYRAgCEAMqDAoGb3JnX2lkEAIYATABOiIKHnNhYXMud29ya19jb250ZXh0LnRhc2tfc3RhcnRlZBACQAFIBVADWARgAXgCgtPkkwIbOgEqIhYvdjEvd29yay1jb250ZXh0czp0YXNrEoYBChVTdGFydEluc3RhbGxhdGlvblRhc2sSLi5zYWFzLmFjY291bnRzLnYxLlN0YXJ0SW5zdGFsbGF0aW9uVGFza1JlcXVlc3QaIy5zYWFzLmFjY291bnRzLnYxLklzc3VlZFdvcmtDb250ZXh0IhjC8xgUCAMQATABOgIQAUABSAdQA1gEYAES6AEKEFN0YXJ0Um9vdFNlc3Npb24SNC5zYWFzLmFjY291bnRzLnYxLlN0YXJ0Um9vdFNlc3Npb25Xb3JrQ29udGV4dFJlcXVlc3QaIy5zYWFzLmFjY291bnRzLnYxLklzc3VlZFdvcmtDb250ZXh0InnC8xhMCAIQAyoMCgZvcmdfaWQQAhgBMAE6Kgomc2Fhcy53b3JrX2NvbnRleHQucm9vdF9zZXNzaW9uX3N0YXJ0ZWQQAkABSAVQBFgEYAF4AoLT5JMCIzoBKiIeL3YxL3dvcmstY29udGV4dHM6cm9vdC1zZXNzaW9uEusBChBFeGNoYW5nZUF1ZGllbmNlEjQuc2Fhcy5hY2NvdW50cy52MS5FeGNoYW5nZVdvcmtDb250ZXh0QXVkaWVuY2VSZXF1ZXN0GiMuc2Fhcy5hY2NvdW50cy52MS5Jc3N1ZWRXb3JrQ29udGV4dCJ8wvMYSggCEAMqDAoGb3JnX2lkEAIYATABOigKJHNhYXMud29ya19jb250ZXh0LmF1ZGllbmNlX2V4Y2hhbmdlZBACQAFIBVAEWARgAXgCgtPkkwIoOgEqIiMvdjEvd29yay1jb250ZXh0czpleGNoYW5nZS1hdWRpZW5jZRLsAQoRU3RhcnRDaGlsZFNlc3Npb24SNS5zYWFzLmFjY291bnRzLnYxLlN0YXJ0Q2hpbGRTZXNzaW9uV29ya0NvbnRleHRSZXF1ZXN0GiMuc2Fhcy5hY2NvdW50cy52MS5Jc3N1ZWRXb3JrQ29udGV4dCJ7wvMYTQgCEAMqDAoGb3JnX2lkEAIYATABOisKJ3NhYXMud29ya19jb250ZXh0LmNoaWxkX3Nlc3Npb25fc3RhcnRlZBACQAFIBVAEWARgAXgCgtPkkwIkOgEqIh8vdjEvd29yay1jb250ZXh0czpjaGlsZC1zZXNzaW9uEskBChBSZW5ld1dvcmtDb250ZXh0Eikuc2Fhcy5hY2NvdW50cy52MS5SZW5ld1dvcmtDb250ZXh0UmVxdWVzdBojLnNhYXMuYWNjb3VudHMudjEuSXNzdWVkV29ya0NvbnRleHQiZcLzGD8IAhADKgwKBm9yZ19pZBACGAEwATodChlzYWFzLndvcmtfY29udGV4dC5yZW5ld2VkEAJAAUgFUARYBGABeAKC0+STAhw6ASoiFy92MS93b3JrLWNvbnRleHRzOnJlbmV3YgZwcm90bzM", [file_buf_validate_validate, file_google_api_annotations, file_google_protobuf_empty, file_google_protobuf_timestamp, file_saas_policy_v1_options]); /** * WorkContextScope is one product-neutral authority slice. Product plugins @@ -123,6 +123,18 @@ export type StartTaskWorkContextRequest = Message<"saas.accounts.v1.StartTaskWor * @generated from field: optional string project_id = 10; */ projectId?: string; + + /** + * Operation bindings of the audience module, by name, whose invoke scopes + * this capability carries beside authority_scopes. The host resolves each + * name to the binding as installed at org_id, narrowed by that installation + * exactly as the module's delegated exchange redeems it, and keeps each + * resource only if the person holds it. The caller names a binding and never + * its resource ids. A direct human-owned mint only. + * + * @generated from field: repeated string operation_bindings = 11; + */ + operationBindings: string[]; }; /**