get_token.go 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412
  1. package registry
  2. import (
  3. "encoding/base64"
  4. "net/http"
  5. "strings"
  6. "time"
  7. "github.com/aws/aws-sdk-go/service/ecr"
  8. "github.com/porter-dev/porter/api/server/handlers"
  9. "github.com/porter-dev/porter/api/server/shared"
  10. "github.com/porter-dev/porter/api/server/shared/apierrors"
  11. "github.com/porter-dev/porter/api/server/shared/config"
  12. "github.com/porter-dev/porter/api/types"
  13. "github.com/porter-dev/porter/internal/models"
  14. "github.com/porter-dev/porter/internal/oauth"
  15. "github.com/porter-dev/porter/internal/registry"
  16. "github.com/aws/aws-sdk-go/aws/arn"
  17. )
  18. type RegistryGetECRTokenHandler struct {
  19. handlers.PorterHandlerReadWriter
  20. }
  21. func NewRegistryGetECRTokenHandler(
  22. config *config.Config,
  23. decoderValidator shared.RequestDecoderValidator,
  24. writer shared.ResultWriter,
  25. ) *RegistryGetECRTokenHandler {
  26. return &RegistryGetECRTokenHandler{
  27. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  28. }
  29. }
  30. func (c *RegistryGetECRTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  31. proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
  32. request := &types.GetRegistryECRTokenRequest{}
  33. if ok := c.DecodeAndValidate(w, r, request); !ok {
  34. return
  35. }
  36. // list registries and find one that matches the region
  37. regs, err := c.Repo().Registry().ListRegistriesByProjectID(proj.ID)
  38. if err != nil {
  39. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  40. return
  41. }
  42. var token string
  43. var expiresAt *time.Time
  44. for _, reg := range regs {
  45. if reg.AWSIntegrationID != 0 {
  46. awsInt, err := c.Repo().AWSIntegration().ReadAWSIntegration(reg.ProjectID, reg.AWSIntegrationID)
  47. if err != nil {
  48. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  49. return
  50. }
  51. // if the aws integration doesn't have an ARN populated, populate it
  52. if awsInt.AWSArn == "" {
  53. err = awsInt.PopulateAWSArn()
  54. if err != nil {
  55. continue
  56. }
  57. }
  58. parsedARN, err := arn.Parse(awsInt.AWSArn)
  59. if err != nil {
  60. continue
  61. }
  62. // if the account id is passed as part of the request, verify the account id matches the account id in the ARN
  63. if awsInt.AWSRegion == request.Region && (request.AccountID == "" || request.AccountID == parsedARN.AccountID) {
  64. // get the aws integration and session
  65. sess, err := awsInt.GetSession()
  66. if err != nil {
  67. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  68. return
  69. }
  70. ecrSvc := ecr.New(sess)
  71. output, err := ecrSvc.GetAuthorizationToken(&ecr.GetAuthorizationTokenInput{})
  72. if err != nil {
  73. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  74. return
  75. }
  76. token = *output.AuthorizationData[0].AuthorizationToken
  77. expiresAt = output.AuthorizationData[0].ExpiresAt
  78. }
  79. }
  80. }
  81. resp := &types.GetRegistryTokenResponse{
  82. Token: token,
  83. ExpiresAt: expiresAt,
  84. }
  85. c.WriteResult(w, r, resp)
  86. }
  87. type RegistryGetGCRTokenHandler struct {
  88. handlers.PorterHandlerReadWriter
  89. }
  90. func NewRegistryGetGCRTokenHandler(
  91. config *config.Config,
  92. decoderValidator shared.RequestDecoderValidator,
  93. writer shared.ResultWriter,
  94. ) *RegistryGetGCRTokenHandler {
  95. return &RegistryGetGCRTokenHandler{
  96. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  97. }
  98. }
  99. func (c *RegistryGetGCRTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  100. proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
  101. request := &types.GetRegistryGCRTokenRequest{}
  102. if ok := c.DecodeAndValidate(w, r, request); !ok {
  103. return
  104. }
  105. // list registries and find one that matches the region
  106. regs, err := c.Repo().Registry().ListRegistriesByProjectID(proj.ID)
  107. if err != nil {
  108. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  109. return
  110. }
  111. var token string
  112. var expiresAt *time.Time
  113. for _, reg := range regs {
  114. if reg.GCPIntegrationID != 0 && strings.Contains(reg.URL, request.ServerURL) {
  115. _reg := registry.Registry(*reg)
  116. oauthTok, err := _reg.GetGCRToken(c.Repo())
  117. // if the oauth token is not nil, but the error is not nil, we still return the token
  118. // but log an error
  119. if oauthTok != nil && err != nil {
  120. c.HandleAPIErrorNoWrite(w, r, apierrors.NewErrInternal(err))
  121. } else if err != nil {
  122. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  123. return
  124. }
  125. token = oauthTok.AccessToken
  126. expiresAt = &oauthTok.Expiry
  127. break
  128. }
  129. }
  130. resp := &types.GetRegistryTokenResponse{
  131. Token: token,
  132. ExpiresAt: expiresAt,
  133. }
  134. c.WriteResult(w, r, resp)
  135. }
  136. type RegistryGetGARTokenHandler struct {
  137. handlers.PorterHandlerReadWriter
  138. }
  139. func NewRegistryGetGARTokenHandler(
  140. config *config.Config,
  141. decoderValidator shared.RequestDecoderValidator,
  142. writer shared.ResultWriter,
  143. ) *RegistryGetGARTokenHandler {
  144. return &RegistryGetGARTokenHandler{
  145. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  146. }
  147. }
  148. func (c *RegistryGetGARTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  149. proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
  150. request := &types.GetRegistryGCRTokenRequest{}
  151. if ok := c.DecodeAndValidate(w, r, request); !ok {
  152. return
  153. }
  154. // list registries and find one that matches the region
  155. regs, err := c.Repo().Registry().ListRegistriesByProjectID(proj.ID)
  156. if err != nil {
  157. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  158. return
  159. }
  160. var token string
  161. var expiresAt *time.Time
  162. for _, reg := range regs {
  163. if reg.GCPIntegrationID != 0 && strings.Contains(reg.URL, request.ServerURL) {
  164. _reg := registry.Registry(*reg)
  165. oauthTok, err := _reg.GetGARToken(c.Repo())
  166. // if the oauth token is not nil, but the error is not nil, we still return the token
  167. // but log an error
  168. if oauthTok != nil && err != nil {
  169. c.HandleAPIErrorNoWrite(w, r, apierrors.NewErrInternal(err))
  170. } else if err != nil {
  171. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  172. return
  173. }
  174. token = oauthTok.AccessToken
  175. expiresAt = &oauthTok.Expiry
  176. break
  177. }
  178. }
  179. resp := &types.GetRegistryTokenResponse{
  180. Token: token,
  181. ExpiresAt: expiresAt,
  182. }
  183. c.WriteResult(w, r, resp)
  184. }
  185. type RegistryGetDOCRTokenHandler struct {
  186. handlers.PorterHandlerReadWriter
  187. }
  188. func NewRegistryGetDOCRTokenHandler(
  189. config *config.Config,
  190. decoderValidator shared.RequestDecoderValidator,
  191. writer shared.ResultWriter,
  192. ) *RegistryGetDOCRTokenHandler {
  193. return &RegistryGetDOCRTokenHandler{
  194. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  195. }
  196. }
  197. func (c *RegistryGetDOCRTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  198. proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
  199. request := &types.GetRegistryDOCRTokenRequest{}
  200. if ok := c.DecodeAndValidate(w, r, request); !ok {
  201. return
  202. }
  203. // list registries and find one that matches the region
  204. regs, err := c.Repo().Registry().ListRegistriesByProjectID(proj.ID)
  205. if err != nil {
  206. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  207. return
  208. }
  209. var token string
  210. var expiresAt *time.Time
  211. for _, reg := range regs {
  212. if reg.DOIntegrationID != 0 && strings.Contains(reg.URL, request.ServerURL) {
  213. oauthInt, err := c.Repo().OAuthIntegration().ReadOAuthIntegration(reg.ProjectID, reg.DOIntegrationID)
  214. if err != nil {
  215. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  216. return
  217. }
  218. tok, expiry, err := oauth.GetAccessToken(
  219. oauthInt.SharedOAuthModel,
  220. c.Config().DOConf,
  221. oauth.MakeUpdateOAuthIntegrationTokenFunction(oauthInt, c.Repo()),
  222. )
  223. if err != nil {
  224. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  225. return
  226. }
  227. token = tok
  228. expiresAt = expiry
  229. break
  230. }
  231. }
  232. resp := &types.GetRegistryTokenResponse{
  233. Token: token,
  234. ExpiresAt: expiresAt,
  235. }
  236. c.WriteResult(w, r, resp)
  237. }
  238. type RegistryGetDockerhubTokenHandler struct {
  239. handlers.PorterHandlerReadWriter
  240. }
  241. func NewRegistryGetDockerhubTokenHandler(
  242. config *config.Config,
  243. decoderValidator shared.RequestDecoderValidator,
  244. writer shared.ResultWriter,
  245. ) *RegistryGetDockerhubTokenHandler {
  246. return &RegistryGetDockerhubTokenHandler{
  247. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  248. }
  249. }
  250. func (c *RegistryGetDockerhubTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  251. proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
  252. // list registries and find one that matches the region
  253. regs, err := c.Repo().Registry().ListRegistriesByProjectID(proj.ID)
  254. if err != nil {
  255. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  256. return
  257. }
  258. var token string
  259. var expiresAt *time.Time
  260. for _, reg := range regs {
  261. if reg.BasicIntegrationID != 0 && strings.Contains(reg.URL, "index.docker.io") {
  262. basic, err := c.Repo().BasicIntegration().ReadBasicIntegration(reg.ProjectID, reg.BasicIntegrationID)
  263. if err != nil {
  264. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  265. return
  266. }
  267. token = base64.StdEncoding.EncodeToString([]byte(string(basic.Username) + ":" + string(basic.Password)))
  268. // we'll just set an arbitrary 30-day expiry time (this is not enforced)
  269. timeExpires := time.Now().Add(30 * 24 * 3600 * time.Second)
  270. expiresAt = &timeExpires
  271. }
  272. }
  273. resp := &types.GetRegistryTokenResponse{
  274. Token: token,
  275. ExpiresAt: expiresAt,
  276. }
  277. c.WriteResult(w, r, resp)
  278. }
  279. type RegistryGetACRTokenHandler struct {
  280. handlers.PorterHandlerReadWriter
  281. }
  282. func NewRegistryGetACRTokenHandler(
  283. config *config.Config,
  284. decoderValidator shared.RequestDecoderValidator,
  285. writer shared.ResultWriter,
  286. ) *RegistryGetACRTokenHandler {
  287. return &RegistryGetACRTokenHandler{
  288. PorterHandlerReadWriter: handlers.NewDefaultPorterHandler(config, decoderValidator, writer),
  289. }
  290. }
  291. func (c *RegistryGetACRTokenHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
  292. proj, _ := r.Context().Value(types.ProjectScope).(*models.Project)
  293. // list registries and find one that matches the region
  294. regs, err := c.Repo().Registry().ListRegistriesByProjectID(proj.ID)
  295. if err != nil {
  296. c.HandleAPIError(w, r, apierrors.NewErrInternal(err))
  297. return
  298. }
  299. var token string
  300. var expiresAt *time.Time
  301. for _, reg := range regs {
  302. if reg.AzureIntegrationID != 0 && strings.Contains(reg.URL, "azurecr.io") {
  303. _reg := registry.Registry(*reg)
  304. username, pw, err := _reg.GetACRCredentials(c.Repo())
  305. if err != nil {
  306. continue
  307. }
  308. token = base64.StdEncoding.EncodeToString([]byte(string(username) + ":" + string(pw)))
  309. // we'll just set an arbitrary 30-day expiry time (this is not enforced)
  310. timeExpires := time.Now().Add(30 * 24 * 3600 * time.Second)
  311. expiresAt = &timeExpires
  312. }
  313. }
  314. resp := &types.GetRegistryTokenResponse{
  315. Token: token,
  316. ExpiresAt: expiresAt,
  317. }
  318. c.WriteResult(w, r, resp)
  319. }