github.com/keybase/client/go@v0.0.0-20240309051027-028f7c731f8b/service/apiserver.go (about) 1 // Copyright 2016 Keybase, Inc. All rights reserved. Use of 2 // this source code is governed by the included BSD license. 3 4 package service 5 6 import ( 7 "encoding/json" 8 "io" 9 10 "github.com/keybase/client/go/libkb" 11 keybase1 "github.com/keybase/client/go/protocol/keybase1" 12 "github.com/keybase/go-framed-msgpack-rpc/rpc" 13 jsonw "github.com/keybase/go-jsonw" 14 "golang.org/x/net/context" 15 ) 16 17 type APIServerHandler struct { 18 *BaseHandler 19 libkb.Contextified 20 } 21 22 func NewAPIServerHandler(xp rpc.Transporter, g *libkb.GlobalContext) *APIServerHandler { 23 return &APIServerHandler{ 24 BaseHandler: NewBaseHandler(g, xp), 25 Contextified: libkb.NewContextified(g), 26 } 27 } 28 29 func (a *APIServerHandler) Get(ctx context.Context, arg keybase1.GetArg) (keybase1.APIRes, error) { 30 mctx := libkb.NewMetaContext(ctx, a.G()) 31 return a.doGet(mctx, arg, false) 32 } 33 34 func (a *APIServerHandler) GetWithSession(ctx context.Context, arg keybase1.GetWithSessionArg) (keybase1.APIRes, error) { 35 mctx := libkb.NewMetaContext(ctx, a.G()) 36 return a.doGet(mctx, arg, true) 37 } 38 39 func (a *APIServerHandler) Post(ctx context.Context, arg keybase1.PostArg) (keybase1.APIRes, error) { 40 mctx := libkb.NewMetaContext(ctx, a.G()) 41 return a.doPost(mctx, arg) 42 } 43 44 func (a *APIServerHandler) PostJSON(ctx context.Context, arg keybase1.PostJSONArg) (keybase1.APIRes, error) { 45 mctx := libkb.NewMetaContext(ctx, a.G()) 46 return a.doPostJSON(mctx, arg) 47 } 48 49 func (a *APIServerHandler) Delete(ctx context.Context, arg keybase1.DeleteArg) (keybase1.APIRes, error) { 50 mctx := libkb.NewMetaContext(ctx, a.G()) 51 return a.doDelete(mctx, arg) 52 } 53 54 type GenericArg interface { 55 GetEndpoint() string 56 GetHTTPArgs() []keybase1.StringKVPair 57 GetHttpStatuses() []int 58 GetAppStatusCodes() []int 59 } 60 61 func (a *APIServerHandler) setupArg(arg GenericArg) libkb.APIArg { 62 // Form http arg dict 63 kbargs := make(libkb.HTTPArgs) 64 for _, harg := range arg.GetHTTPArgs() { 65 kbargs[harg.Key] = libkb.S{Val: harg.Value} 66 } 67 68 // Acceptable http status list 69 s := arg.GetHttpStatuses() 70 httpStatuses := make([]int, len(s)) 71 copy(httpStatuses, s) 72 73 // Acceptable app status code list 74 c := arg.GetAppStatusCodes() 75 appStatusCodes := make([]int, len(c)) 76 copy(appStatusCodes, c) 77 78 // Do the API call 79 kbarg := libkb.APIArg{ 80 Endpoint: arg.GetEndpoint(), 81 SessionType: libkb.APISessionTypeREQUIRED, 82 Args: kbargs, 83 HTTPStatus: httpStatuses, 84 AppStatusCodes: appStatusCodes, 85 } 86 87 return kbarg 88 } 89 90 func (a *APIServerHandler) doGet(mctx libkb.MetaContext, arg GenericArg, sessionRequired bool) (res keybase1.APIRes, err error) { 91 defer mctx.Trace("APIServerHandler::Get", &err)() 92 // turn off session requirement if not needed 93 kbarg := a.setupArg(arg) 94 if !sessionRequired { 95 kbarg.SessionType = libkb.APISessionTypeNONE 96 } 97 if getWithSessionArg, ok := arg.(keybase1.GetWithSessionArg); ok && getWithSessionArg.UseText != nil && *getWithSessionArg.UseText { 98 kbarg.UseText = true 99 resp, finisher, err := mctx.G().API.GetResp(mctx, kbarg) 100 defer finisher() 101 if err != nil { 102 return res, err 103 } 104 body, err := io.ReadAll(resp.Body) 105 if err != nil { 106 return res, err 107 } 108 return keybase1.APIRes{ 109 Body: string(body), 110 HttpStatus: resp.StatusCode, 111 }, nil 112 } 113 var ires *libkb.APIRes 114 ires, err = mctx.G().API.Get(mctx, kbarg) 115 if err != nil { 116 return res, err 117 } 118 return a.convertRes(ires), nil 119 } 120 121 func (a *APIServerHandler) doPost(mctx libkb.MetaContext, arg keybase1.PostArg) (res keybase1.APIRes, err error) { 122 defer mctx.Trace("APIServerHandler::Post", &err)() 123 var ires *libkb.APIRes 124 ires, err = mctx.G().API.Post(mctx, a.setupArg(arg)) 125 if err != nil { 126 return res, err 127 } 128 return a.convertRes(ires), nil 129 } 130 131 func (a *APIServerHandler) doPostJSON(mctx libkb.MetaContext, rawarg keybase1.PostJSONArg) (res keybase1.APIRes, err error) { 132 defer mctx.Trace("APIServerHandler::PostJSON", &err)() 133 var ires *libkb.APIRes 134 arg := a.setupArg(rawarg) 135 jsonPayload := make(libkb.JSONPayload) 136 for _, kvpair := range rawarg.JSONPayload { 137 var value interface{} 138 err = jsonw.EnsureMaxDepthBytesDefault([]byte(kvpair.Value)) 139 if err != nil { 140 return keybase1.APIRes{}, err 141 } 142 err := json.Unmarshal([]byte(kvpair.Value), &value) 143 if err != nil { 144 return keybase1.APIRes{}, err 145 } 146 jsonPayload[kvpair.Key] = value 147 } 148 arg.JSONPayload = jsonPayload 149 150 ires, err = mctx.G().API.PostJSON(mctx, arg) 151 if err != nil { 152 return keybase1.APIRes{}, err 153 } 154 155 return a.convertRes(ires), nil 156 } 157 158 func (a *APIServerHandler) doDelete(mctx libkb.MetaContext, arg keybase1.DeleteArg) (res keybase1.APIRes, err error) { 159 a.G().Trace("APIServerHandler::Delete", &err)() 160 var ires *libkb.APIRes 161 ires, err = a.G().API.Delete(mctx, a.setupArg(arg)) 162 if err != nil { 163 return res, err 164 } 165 return a.convertRes(ires), nil 166 } 167 168 func (a *APIServerHandler) convertRes(res *libkb.APIRes) keybase1.APIRes { 169 // Translate the result 170 var ares keybase1.APIRes 171 mstatus, err := res.Status.Marshal() 172 if err == nil { 173 ares.Status = string(mstatus) 174 } 175 mbody, err := res.Body.Marshal() 176 if err == nil { 177 ares.Body = string(mbody) 178 } 179 ares.HttpStatus = res.HTTPStatus 180 181 appStatus := jsonw.NewWrapper(res.AppStatus) 182 mappstatus, err := appStatus.Marshal() 183 if err == nil { 184 ares.AppStatus = string(mappstatus) 185 } 186 187 return ares 188 }