auth_test.go 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655
  1. // Copyright 2015 CoreOS, Inc.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package auth
  15. import (
  16. "reflect"
  17. "testing"
  18. "time"
  19. "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/crypto/bcrypt"
  20. "github.com/coreos/etcd/Godeps/_workspace/src/golang.org/x/net/context"
  21. etcderr "github.com/coreos/etcd/error"
  22. "github.com/coreos/etcd/etcdserver"
  23. "github.com/coreos/etcd/etcdserver/etcdserverpb"
  24. "github.com/coreos/etcd/store"
  25. )
  26. const testTimeout = time.Millisecond
  27. func TestMergeUser(t *testing.T) {
  28. tbl := []struct {
  29. input User
  30. merge User
  31. expect User
  32. iserr bool
  33. }{
  34. {
  35. User{User: "foo"},
  36. User{User: "bar"},
  37. User{},
  38. true,
  39. },
  40. {
  41. User{User: "foo"},
  42. User{User: "foo"},
  43. User{User: "foo", Roles: []string{}},
  44. false,
  45. },
  46. {
  47. User{User: "foo"},
  48. User{User: "foo", Grant: []string{"role1"}},
  49. User{User: "foo", Roles: []string{"role1"}},
  50. false,
  51. },
  52. {
  53. User{User: "foo", Roles: []string{"role1"}},
  54. User{User: "foo", Grant: []string{"role1"}},
  55. User{},
  56. true,
  57. },
  58. {
  59. User{User: "foo", Roles: []string{"role1"}},
  60. User{User: "foo", Revoke: []string{"role2"}},
  61. User{},
  62. true,
  63. },
  64. {
  65. User{User: "foo", Roles: []string{"role1"}},
  66. User{User: "foo", Grant: []string{"role2"}},
  67. User{User: "foo", Roles: []string{"role1", "role2"}},
  68. false,
  69. },
  70. {
  71. User{User: "foo"},
  72. User{User: "foo", Password: "bar"},
  73. User{User: "foo", Roles: []string{}, Password: "$2a$10$aUPOdbOGNawaVSusg3g2wuC3AH6XxIr9/Ms4VgDvzrAVOJPYzZILa"},
  74. false,
  75. },
  76. }
  77. for i, tt := range tbl {
  78. out, err := tt.input.merge(tt.merge)
  79. if err != nil && !tt.iserr {
  80. t.Fatalf("Got unexpected error on item %d", i)
  81. }
  82. if !tt.iserr {
  83. err := bcrypt.CompareHashAndPassword([]byte(out.Password), []byte(tt.merge.Password))
  84. if err == nil {
  85. tt.expect.Password = out.Password
  86. }
  87. if !reflect.DeepEqual(out, tt.expect) {
  88. t.Errorf("Unequal merge expectation on item %d: got: %#v, expect: %#v", i, out, tt.expect)
  89. }
  90. }
  91. }
  92. }
  93. func TestMergeRole(t *testing.T) {
  94. tbl := []struct {
  95. input Role
  96. merge Role
  97. expect Role
  98. iserr bool
  99. }{
  100. {
  101. Role{Role: "foo"},
  102. Role{Role: "bar"},
  103. Role{},
  104. true,
  105. },
  106. {
  107. Role{Role: "foo"},
  108. Role{Role: "foo", Grant: &Permissions{KV: rwPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
  109. Role{Role: "foo", Permissions: Permissions{KV: rwPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
  110. false,
  111. },
  112. {
  113. Role{Role: "foo", Permissions: Permissions{KV: rwPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
  114. Role{Role: "foo", Revoke: &Permissions{KV: rwPermission{Read: []string{"/foodir"}, Write: []string{"/foodir"}}}},
  115. Role{Role: "foo", Permissions: Permissions{KV: rwPermission{Read: []string{}, Write: []string{}}}},
  116. false,
  117. },
  118. {
  119. Role{Role: "foo", Permissions: Permissions{KV: rwPermission{Read: []string{"/bardir"}}}},
  120. Role{Role: "foo", Revoke: &Permissions{KV: rwPermission{Read: []string{"/foodir"}}}},
  121. Role{},
  122. true,
  123. },
  124. }
  125. for i, tt := range tbl {
  126. out, err := tt.input.merge(tt.merge)
  127. if err != nil && !tt.iserr {
  128. t.Fatalf("Got unexpected error on item %d", i)
  129. }
  130. if !tt.iserr {
  131. if !reflect.DeepEqual(out, tt.expect) {
  132. t.Errorf("Unequal merge expectation on item %d: got: %#v, expect: %#v", i, out, tt.expect)
  133. }
  134. }
  135. }
  136. }
  137. type testDoer struct {
  138. get []etcdserver.Response
  139. put []etcdserver.Response
  140. getindex int
  141. putindex int
  142. explicitlyEnabled bool
  143. }
  144. func (td *testDoer) Do(_ context.Context, req etcdserverpb.Request) (etcdserver.Response, error) {
  145. if td.explicitlyEnabled && (req.Path == StorePermsPrefix+"/enabled") {
  146. t := "true"
  147. return etcdserver.Response{
  148. Event: &store.Event{
  149. Action: store.Get,
  150. Node: &store.NodeExtern{
  151. Key: StorePermsPrefix + "/users/cat",
  152. Value: &t,
  153. },
  154. },
  155. }, nil
  156. }
  157. if req.Method == "GET" && td.get != nil {
  158. res := td.get[td.getindex]
  159. if res.Event == nil {
  160. td.getindex++
  161. return etcdserver.Response{}, &etcderr.Error{
  162. ErrorCode: etcderr.EcodeKeyNotFound,
  163. }
  164. }
  165. td.getindex++
  166. return res, nil
  167. }
  168. if req.Method == "PUT" && td.put != nil {
  169. res := td.put[td.putindex]
  170. if res.Event == nil {
  171. td.putindex++
  172. return etcdserver.Response{}, &etcderr.Error{
  173. ErrorCode: etcderr.EcodeNodeExist,
  174. }
  175. }
  176. td.putindex++
  177. return res, nil
  178. }
  179. return etcdserver.Response{}, nil
  180. }
  181. func TestAllUsers(t *testing.T) {
  182. d := &testDoer{
  183. get: []etcdserver.Response{
  184. {
  185. Event: &store.Event{
  186. Action: store.Get,
  187. Node: &store.NodeExtern{
  188. Nodes: store.NodeExterns([]*store.NodeExtern{
  189. &store.NodeExtern{
  190. Key: StorePermsPrefix + "/users/cat",
  191. },
  192. &store.NodeExtern{
  193. Key: StorePermsPrefix + "/users/dog",
  194. },
  195. }),
  196. },
  197. },
  198. },
  199. },
  200. }
  201. expected := []string{"cat", "dog"}
  202. s := Store{d, testTimeout, false}
  203. users, err := s.AllUsers()
  204. if err != nil {
  205. t.Error("Unexpected error", err)
  206. }
  207. if !reflect.DeepEqual(users, expected) {
  208. t.Error("AllUsers doesn't match given store. Got", users, "expected", expected)
  209. }
  210. }
  211. func TestGetAndDeleteUser(t *testing.T) {
  212. data := `{"user": "cat", "roles" : ["animal"]}`
  213. d := &testDoer{
  214. get: []etcdserver.Response{
  215. {
  216. Event: &store.Event{
  217. Action: store.Get,
  218. Node: &store.NodeExtern{
  219. Key: StorePermsPrefix + "/users/cat",
  220. Value: &data,
  221. },
  222. },
  223. },
  224. },
  225. explicitlyEnabled: true,
  226. }
  227. expected := User{User: "cat", Roles: []string{"animal"}}
  228. s := Store{d, testTimeout, false}
  229. out, err := s.GetUser("cat")
  230. if err != nil {
  231. t.Error("Unexpected error", err)
  232. }
  233. if !reflect.DeepEqual(out, expected) {
  234. t.Error("GetUser doesn't match given store. Got", out, "expected", expected)
  235. }
  236. err = s.DeleteUser("cat")
  237. if err != nil {
  238. t.Error("Unexpected error", err)
  239. }
  240. }
  241. func TestAllRoles(t *testing.T) {
  242. d := &testDoer{
  243. get: []etcdserver.Response{
  244. {
  245. Event: &store.Event{
  246. Action: store.Get,
  247. Node: &store.NodeExtern{
  248. Nodes: store.NodeExterns([]*store.NodeExtern{
  249. &store.NodeExtern{
  250. Key: StorePermsPrefix + "/roles/animal",
  251. },
  252. &store.NodeExtern{
  253. Key: StorePermsPrefix + "/roles/human",
  254. },
  255. }),
  256. },
  257. },
  258. },
  259. },
  260. explicitlyEnabled: true,
  261. }
  262. expected := []string{"animal", "human", "root"}
  263. s := Store{d, testTimeout, false}
  264. out, err := s.AllRoles()
  265. if err != nil {
  266. t.Error("Unexpected error", err)
  267. }
  268. if !reflect.DeepEqual(out, expected) {
  269. t.Error("AllRoles doesn't match given store. Got", out, "expected", expected)
  270. }
  271. }
  272. func TestGetAndDeleteRole(t *testing.T) {
  273. data := `{"role": "animal"}`
  274. d := &testDoer{
  275. get: []etcdserver.Response{
  276. {
  277. Event: &store.Event{
  278. Action: store.Get,
  279. Node: &store.NodeExtern{
  280. Key: StorePermsPrefix + "/roles/animal",
  281. Value: &data,
  282. },
  283. },
  284. },
  285. },
  286. explicitlyEnabled: true,
  287. }
  288. expected := Role{Role: "animal"}
  289. s := Store{d, testTimeout, false}
  290. out, err := s.GetRole("animal")
  291. if err != nil {
  292. t.Error("Unexpected error", err)
  293. }
  294. if !reflect.DeepEqual(out, expected) {
  295. t.Error("GetRole doesn't match given store. Got", out, "expected", expected)
  296. }
  297. err = s.DeleteRole("animal")
  298. if err != nil {
  299. t.Error("Unexpected error", err)
  300. }
  301. }
  302. func TestEnsure(t *testing.T) {
  303. d := &testDoer{
  304. get: []etcdserver.Response{
  305. {
  306. Event: &store.Event{
  307. Action: store.Set,
  308. Node: &store.NodeExtern{
  309. Key: StorePermsPrefix,
  310. Dir: true,
  311. },
  312. },
  313. },
  314. {
  315. Event: &store.Event{
  316. Action: store.Set,
  317. Node: &store.NodeExtern{
  318. Key: StorePermsPrefix + "/users/",
  319. Dir: true,
  320. },
  321. },
  322. },
  323. {
  324. Event: &store.Event{
  325. Action: store.Set,
  326. Node: &store.NodeExtern{
  327. Key: StorePermsPrefix + "/roles/",
  328. Dir: true,
  329. },
  330. },
  331. },
  332. },
  333. }
  334. s := Store{d, testTimeout, false}
  335. err := s.ensureAuthDirectories()
  336. if err != nil {
  337. t.Error("Unexpected error", err)
  338. }
  339. }
  340. func TestCreateAndUpdateUser(t *testing.T) {
  341. olduser := `{"user": "cat", "roles" : ["animal"]}`
  342. newuser := `{"user": "cat", "roles" : ["animal", "pet"]}`
  343. d := &testDoer{
  344. get: []etcdserver.Response{
  345. {
  346. Event: nil,
  347. },
  348. {
  349. Event: &store.Event{
  350. Action: store.Get,
  351. Node: &store.NodeExtern{
  352. Key: StorePermsPrefix + "/users/cat",
  353. Value: &olduser,
  354. },
  355. },
  356. },
  357. {
  358. Event: &store.Event{
  359. Action: store.Get,
  360. Node: &store.NodeExtern{
  361. Key: StorePermsPrefix + "/users/cat",
  362. Value: &olduser,
  363. },
  364. },
  365. },
  366. },
  367. put: []etcdserver.Response{
  368. {
  369. Event: &store.Event{
  370. Action: store.Update,
  371. Node: &store.NodeExtern{
  372. Key: StorePermsPrefix + "/users/cat",
  373. Value: &olduser,
  374. },
  375. },
  376. },
  377. {
  378. Event: &store.Event{
  379. Action: store.Update,
  380. Node: &store.NodeExtern{
  381. Key: StorePermsPrefix + "/users/cat",
  382. Value: &newuser,
  383. },
  384. },
  385. },
  386. },
  387. explicitlyEnabled: true,
  388. }
  389. user := User{User: "cat", Password: "meow", Roles: []string{"animal"}}
  390. update := User{User: "cat", Grant: []string{"pet"}}
  391. expected := User{User: "cat", Roles: []string{"animal", "pet"}}
  392. s := Store{d, testTimeout, true}
  393. out, created, err := s.CreateOrUpdateUser(user)
  394. if created == false {
  395. t.Error("Should have created user, instead updated?")
  396. }
  397. if err != nil {
  398. t.Error("Unexpected error", err)
  399. }
  400. out.Password = "meow"
  401. if !reflect.DeepEqual(out, user) {
  402. t.Error("UpdateUser doesn't match given update. Got", out, "expected", expected)
  403. }
  404. out, created, err = s.CreateOrUpdateUser(update)
  405. if created == true {
  406. t.Error("Should have updated user, instead created?")
  407. }
  408. if err != nil {
  409. t.Error("Unexpected error", err)
  410. }
  411. if !reflect.DeepEqual(out, expected) {
  412. t.Error("UpdateUser doesn't match given update. Got", out, "expected", expected)
  413. }
  414. }
  415. func TestUpdateRole(t *testing.T) {
  416. oldrole := `{"role": "animal", "permissions" : {"kv": {"read": ["/animal"], "write": []}}}`
  417. newrole := `{"role": "animal", "permissions" : {"kv": {"read": ["/animal"], "write": ["/animal"]}}}`
  418. d := &testDoer{
  419. get: []etcdserver.Response{
  420. {
  421. Event: &store.Event{
  422. Action: store.Get,
  423. Node: &store.NodeExtern{
  424. Key: StorePermsPrefix + "/roles/animal",
  425. Value: &oldrole,
  426. },
  427. },
  428. },
  429. },
  430. put: []etcdserver.Response{
  431. {
  432. Event: &store.Event{
  433. Action: store.Update,
  434. Node: &store.NodeExtern{
  435. Key: StorePermsPrefix + "/roles/animal",
  436. Value: &newrole,
  437. },
  438. },
  439. },
  440. },
  441. explicitlyEnabled: true,
  442. }
  443. update := Role{Role: "animal", Grant: &Permissions{KV: rwPermission{Read: []string{}, Write: []string{"/animal"}}}}
  444. expected := Role{Role: "animal", Permissions: Permissions{KV: rwPermission{Read: []string{"/animal"}, Write: []string{"/animal"}}}}
  445. s := Store{d, testTimeout, true}
  446. out, err := s.UpdateRole(update)
  447. if err != nil {
  448. t.Error("Unexpected error", err)
  449. }
  450. if !reflect.DeepEqual(out, expected) {
  451. t.Error("UpdateRole doesn't match given update. Got", out, "expected", expected)
  452. }
  453. }
  454. func TestCreateRole(t *testing.T) {
  455. role := `{"role": "animal", "permissions" : {"kv": {"read": ["/animal"], "write": []}}}`
  456. d := &testDoer{
  457. put: []etcdserver.Response{
  458. {
  459. Event: &store.Event{
  460. Action: store.Create,
  461. Node: &store.NodeExtern{
  462. Key: StorePermsPrefix + "/roles/animal",
  463. Value: &role,
  464. },
  465. },
  466. },
  467. {
  468. Event: nil,
  469. },
  470. },
  471. explicitlyEnabled: true,
  472. }
  473. r := Role{Role: "animal", Permissions: Permissions{KV: rwPermission{Read: []string{"/animal"}, Write: []string{}}}}
  474. s := Store{d, testTimeout, true}
  475. err := s.CreateRole(Role{Role: "root"})
  476. if err == nil {
  477. t.Error("Should error creating root role")
  478. }
  479. err = s.CreateRole(r)
  480. if err != nil {
  481. t.Error("Unexpected error", err)
  482. }
  483. err = s.CreateRole(r)
  484. if err == nil {
  485. t.Error("Creating duplicate role, should error")
  486. }
  487. }
  488. func TestEnableAuth(t *testing.T) {
  489. rootUser := `{"user": "root", "password": ""}`
  490. guestRole := `{"role": "guest", "permissions" : {"kv": {"read": ["*"], "write": ["*"]}}}`
  491. trueval := "true"
  492. falseval := "false"
  493. d := &testDoer{
  494. get: []etcdserver.Response{
  495. {
  496. Event: &store.Event{
  497. Action: store.Get,
  498. Node: &store.NodeExtern{
  499. Key: StorePermsPrefix + "/enabled",
  500. Value: &falseval,
  501. },
  502. },
  503. },
  504. {
  505. Event: &store.Event{
  506. Action: store.Get,
  507. Node: &store.NodeExtern{
  508. Key: StorePermsPrefix + "/user/root",
  509. Value: &rootUser,
  510. },
  511. },
  512. },
  513. {
  514. Event: nil,
  515. },
  516. },
  517. put: []etcdserver.Response{
  518. {
  519. Event: &store.Event{
  520. Action: store.Create,
  521. Node: &store.NodeExtern{
  522. Key: StorePermsPrefix + "/roles/guest",
  523. Value: &guestRole,
  524. },
  525. },
  526. },
  527. {
  528. Event: &store.Event{
  529. Action: store.Update,
  530. Node: &store.NodeExtern{
  531. Key: StorePermsPrefix + "/enabled",
  532. Value: &trueval,
  533. },
  534. },
  535. },
  536. },
  537. explicitlyEnabled: false,
  538. }
  539. s := Store{d, testTimeout, true}
  540. err := s.EnableAuth()
  541. if err != nil {
  542. t.Error("Unexpected error", err)
  543. }
  544. }
  545. func TestDisableAuth(t *testing.T) {
  546. trueval := "true"
  547. falseval := "false"
  548. d := &testDoer{
  549. get: []etcdserver.Response{
  550. {
  551. Event: &store.Event{
  552. Action: store.Get,
  553. Node: &store.NodeExtern{
  554. Key: StorePermsPrefix + "/enabled",
  555. Value: &falseval,
  556. },
  557. },
  558. },
  559. {
  560. Event: &store.Event{
  561. Action: store.Get,
  562. Node: &store.NodeExtern{
  563. Key: StorePermsPrefix + "/enabled",
  564. Value: &trueval,
  565. },
  566. },
  567. },
  568. },
  569. put: []etcdserver.Response{
  570. {
  571. Event: &store.Event{
  572. Action: store.Update,
  573. Node: &store.NodeExtern{
  574. Key: StorePermsPrefix + "/enabled",
  575. Value: &falseval,
  576. },
  577. },
  578. },
  579. },
  580. explicitlyEnabled: false,
  581. }
  582. s := Store{d, testTimeout, true}
  583. err := s.DisableAuth()
  584. if err == nil {
  585. t.Error("Expected error; already disabled")
  586. }
  587. err = s.DisableAuth()
  588. if err != nil {
  589. t.Error("Unexpected error", err)
  590. }
  591. }
  592. func TestSimpleMatch(t *testing.T) {
  593. role := Role{Role: "foo", Permissions: Permissions{KV: rwPermission{Read: []string{"/foodir/*", "/fookey"}, Write: []string{"/bardir/*", "/barkey"}}}}
  594. if !role.HasKeyAccess("/foodir/foo/bar", false) {
  595. t.Fatal("role lacks expected access")
  596. }
  597. if !role.HasKeyAccess("/fookey", false) {
  598. t.Fatal("role lacks expected access")
  599. }
  600. if !role.HasRecursiveAccess("/foodir/*", false) {
  601. t.Fatal("role lacks expected access")
  602. }
  603. if !role.HasRecursiveAccess("/foodir/foo*", false) {
  604. t.Fatal("role lacks expected access")
  605. }
  606. if !role.HasRecursiveAccess("/bardir/*", true) {
  607. t.Fatal("role lacks expected access")
  608. }
  609. if !role.HasKeyAccess("/bardir/bar/foo", true) {
  610. t.Fatal("role lacks expected access")
  611. }
  612. if !role.HasKeyAccess("/barkey", true) {
  613. t.Fatal("role lacks expected access")
  614. }
  615. if role.HasKeyAccess("/bardir/bar/foo", false) {
  616. t.Fatal("role has unexpected access")
  617. }
  618. if role.HasKeyAccess("/barkey", false) {
  619. t.Fatal("role has unexpected access")
  620. }
  621. if role.HasKeyAccess("/foodir/foo/bar", true) {
  622. t.Fatal("role has unexpected access")
  623. }
  624. if role.HasKeyAccess("/fookey", true) {
  625. t.Fatal("role has unexpected access")
  626. }
  627. }