oidc_test.go 7.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273
  1. /*
  2. * Licensed to the Apache Software Foundation (ASF) under one or more
  3. * contributor license agreements. See the NOTICE file distributed with
  4. * this work for additional information regarding copyright ownership.
  5. * The ASF licenses this file to You under the Apache License, Version 2.0
  6. * (the "License"); you may not use this file except in compliance with
  7. * the License. You may obtain a copy of the License at
  8. *
  9. * http://www.apache.org/licenses/LICENSE-2.0
  10. *
  11. * Unless required by applicable law or agreed to in writing, software
  12. * distributed under the License is distributed on an "AS IS" BASIS,
  13. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  14. * See the License for the specific language governing permissions and
  15. * limitations under the License.
  16. */
  17. package oidc_test
  18. import (
  19. "bytes"
  20. "context"
  21. "io/ioutil"
  22. "math/rand"
  23. "net/http"
  24. "net/url"
  25. "strings"
  26. "time"
  27. "github.com/Nerzal/gocloak/v11"
  28. "github.com/PuerkitoBio/goquery"
  29. . "github.com/onsi/ginkgo/v2"
  30. . "github.com/onsi/gomega"
  31. )
  32. var _ = Describe("Oidc-Login", func() {
  33. var (
  34. OidcCookie []http.Cookie
  35. )
  36. Context("test apisix/admin/oidc/login", func() {
  37. It("should return status-code 302", func() {
  38. statusCode, err := accessOidcLogin()
  39. Expect(err).ShouldNot(HaveOccurred(), "do request")
  40. Expect(statusCode).To(Equal(http.StatusFound))
  41. })
  42. })
  43. Context("test apisix/admin/oidc/callback", func() {
  44. It("should return status-code 200", func() {
  45. statusCode, err := accessOidcCallback(&OidcCookie)
  46. Expect(err).ShouldNot(HaveOccurred(), "do request")
  47. Expect(statusCode).To(Equal(http.StatusOK))
  48. })
  49. })
  50. Context("access apisix/admin/routes with cookie", func() {
  51. It("should return status-code 200", func() {
  52. statusCode, err := accessRoutesWithCookie(true, OidcCookie)
  53. Expect(err).ShouldNot(HaveOccurred(), "do request")
  54. Expect(statusCode).To(Equal(http.StatusOK))
  55. })
  56. })
  57. Context("access apisix/admin/oidc/logout with cookie", func() {
  58. It("should return status-code 200", func() {
  59. statusCode, err := accessOidcLogoutWithCookie(true, OidcCookie)
  60. Expect(err).ShouldNot(HaveOccurred(), "do request")
  61. Expect(statusCode).To(Equal(http.StatusOK))
  62. })
  63. })
  64. Context("access apisix/admin/routes with invalid cookie", func() {
  65. It("should return status-code 401", func() {
  66. statusCode, err := accessRoutesWithCookie(false, OidcCookie)
  67. Expect(err).ShouldNot(HaveOccurred(), "do request")
  68. Expect(statusCode).To(Equal(http.StatusUnauthorized))
  69. })
  70. })
  71. Context("access apisix/admin/oidc/logout with invalid cookie", func() {
  72. It("should return status-code 403", func() {
  73. statusCode, err := accessOidcLogoutWithCookie(false, OidcCookie)
  74. Expect(err).ShouldNot(HaveOccurred(), "do request")
  75. Expect(statusCode).To(Equal(http.StatusForbidden))
  76. })
  77. })
  78. })
  79. func accessOidcLogin() (int, error) {
  80. var err error
  81. var req *http.Request
  82. var resp *http.Response
  83. var Client = &http.Client{
  84. CheckRedirect: func(req *http.Request, via []*http.Request) error {
  85. return http.ErrUseLastResponse
  86. },
  87. }
  88. createClientAndUser()
  89. req, _ = http.NewRequest("GET", "http://127.0.0.1:9000/apisix/admin/oidc/login", nil)
  90. resp, err = Client.Do(req)
  91. if err != nil {
  92. return 0, err
  93. }
  94. // return status-code
  95. return resp.StatusCode, err
  96. }
  97. func createClientAndUser() string {
  98. client := gocloak.NewClient("http://127.0.0.1:8080")
  99. ctx := context.Background()
  100. token, _ := client.LoginAdmin(ctx, "admin", "admin", "master")
  101. redirectURIs := []string{"http://127.0.0.1:9000/*"}
  102. _, _ = client.CreateClient(ctx, token.AccessToken, "master", gocloak.Client{
  103. ClientID: gocloak.StringP("dashboard"),
  104. Secret: gocloak.StringP("dashboard"),
  105. RedirectURIs: &redirectURIs,
  106. })
  107. username := GetRandomString(3)
  108. user := gocloak.User{
  109. FirstName: gocloak.StringP(GetRandomString(3)),
  110. LastName: gocloak.StringP(GetRandomString(3)),
  111. Email: gocloak.StringP(GetRandomString(3)),
  112. Enabled: gocloak.BoolP(true),
  113. Username: gocloak.StringP(username),
  114. }
  115. id, _ := client.CreateUser(ctx, token.AccessToken, "master", user)
  116. _ = client.SetPassword(ctx, token.AccessToken, id, "master", "password", false)
  117. return username
  118. }
  119. func accessOidcCallback(OidcCookie *[]http.Cookie) (int, error) {
  120. var authenticationUrl string
  121. var loginUrl string
  122. var err error
  123. var req *http.Request
  124. var resp *http.Response
  125. var client = &http.Client{
  126. CheckRedirect: func(req *http.Request, via []*http.Request) error {
  127. return http.ErrUseLastResponse
  128. },
  129. }
  130. username := createClientAndUser()
  131. // access apisix/admin/oidc/login to get the authentication-url
  132. req, _ = http.NewRequest("GET", "http://127.0.0.1:9000/apisix/admin/oidc/login", nil)
  133. resp, err = client.Do(req)
  134. if err != nil {
  135. return 0, err
  136. }
  137. authenticationUrl = resp.Header.Get("Location")
  138. // access the authentication-url
  139. req, _ = http.NewRequest("GET", authenticationUrl, nil)
  140. resp, err = client.Do(req)
  141. if err != nil {
  142. return 0, err
  143. }
  144. // get the login-url from html
  145. body, _ := ioutil.ReadAll(resp.Body)
  146. dom, _ := goquery.NewDocumentFromReader(strings.NewReader(string(body)))
  147. dom.Find("#kc-form-login").Each(func(i int, selection *goquery.Selection) {
  148. loginUrl = selection.Get(0).Attr[2].Val
  149. })
  150. // set username & password
  151. formValues := url.Values{}
  152. formValues.Set("username", username)
  153. formValues.Set("password", "password")
  154. formDataStr := formValues.Encode()
  155. formDataBytes := []byte(formDataStr)
  156. formBytesReader := bytes.NewReader(formDataBytes)
  157. //fmt.Printf("loginUrl: %s/n", loginUrl)
  158. req, _ = http.NewRequest("POST", loginUrl, formBytesReader)
  159. req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
  160. // set cookies
  161. cookies := resp.Cookies()
  162. for _, cookie := range cookies {
  163. req.AddCookie(cookie)
  164. }
  165. // access the login-url to login
  166. resp, err = client.Do(req)
  167. if err != nil {
  168. return 0, err
  169. }
  170. // access apisix/admin/oidc/login with code
  171. callbackUrl := resp.Header.Get("Location")
  172. req, _ = http.NewRequest("GET", callbackUrl, nil)
  173. resp, err = client.Do(req)
  174. if err != nil {
  175. return 0, err
  176. }
  177. // save cookie
  178. cookies = resp.Cookies()
  179. for _, cookie := range cookies {
  180. *OidcCookie = append(*OidcCookie, *cookie)
  181. }
  182. // return status-code
  183. return resp.StatusCode, err
  184. }
  185. func accessRoutesWithCookie(setCookie bool, OidcCookie []http.Cookie) (int, error) {
  186. var err error
  187. var req *http.Request
  188. var resp *http.Response
  189. var client http.Client
  190. req, _ = http.NewRequest("GET", "http://127.0.0.1:9000/apisix/admin/routes", nil)
  191. // set cookie or not
  192. if setCookie {
  193. for _, cookie := range OidcCookie {
  194. req.AddCookie(&cookie)
  195. }
  196. }
  197. // access apisix/admin/routes
  198. resp, err = client.Do(req)
  199. if err != nil {
  200. return 0, err
  201. }
  202. // return status-code
  203. return resp.StatusCode, err
  204. }
  205. func accessOidcLogoutWithCookie(setCookie bool, OidcCookie []http.Cookie) (int, error) {
  206. var err error
  207. var req *http.Request
  208. var resp *http.Response
  209. var client http.Client
  210. req, _ = http.NewRequest("GET", "http://127.0.0.1:9000/apisix/admin/oidc/logout", nil)
  211. // set cookie or not
  212. if setCookie {
  213. for _, cookie := range OidcCookie {
  214. req.AddCookie(&cookie)
  215. }
  216. }
  217. // access apisix/admin/oidc/logout
  218. resp, err = client.Do(req)
  219. if err != nil {
  220. return 0, err
  221. }
  222. // return status-code
  223. return resp.StatusCode, err
  224. }
  225. func GetRandomString(l int) string {
  226. str := "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ"
  227. bytes := []byte(str)
  228. var result []byte
  229. r := rand.New(rand.NewSource(time.Now().UnixNano()))
  230. for i := 0; i < l; i++ {
  231. result = append(result, bytes[r.Intn(len(bytes))])
  232. }
  233. return string(result)
  234. }