blob: d6574ecca631719a0aa401c1e210b9f8c1dc24a8 [file] [log] [blame]
khenaidooab1f7bd2019-11-14 14:00:27 -05001// Copyright 2015 The etcd Authors
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
15package v2auth
16
17import (
18 "context"
19 "encoding/json"
20 "path"
21
22 "go.etcd.io/etcd/etcdserver"
23 "go.etcd.io/etcd/etcdserver/api/v2error"
24 "go.etcd.io/etcd/etcdserver/etcdserverpb"
25
26 "go.uber.org/zap"
27)
28
29func (s *store) ensureAuthDirectories() error {
30 if s.ensuredOnce {
31 return nil
32 }
33 for _, res := range []string{StorePermsPrefix, StorePermsPrefix + "/users/", StorePermsPrefix + "/roles/"} {
34 ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
35 pe := false
36 rr := etcdserverpb.Request{
37 Method: "PUT",
38 Path: res,
39 Dir: true,
40 PrevExist: &pe,
41 }
42 _, err := s.server.Do(ctx, rr)
43 cancel()
44 if err != nil {
45 if e, ok := err.(*v2error.Error); ok {
46 if e.ErrorCode == v2error.EcodeNodeExist {
47 continue
48 }
49 }
50 if s.lg != nil {
51 s.lg.Warn(
52 "failed to create auth directories",
53 zap.Error(err),
54 )
55 } else {
56 plog.Errorf("failed to create auth directories in the store (%v)", err)
57 }
58 return err
59 }
60 }
61 ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
62 defer cancel()
63 pe := false
64 rr := etcdserverpb.Request{
65 Method: "PUT",
66 Path: StorePermsPrefix + "/enabled",
67 Val: "false",
68 PrevExist: &pe,
69 }
70 _, err := s.server.Do(ctx, rr)
71 if err != nil {
72 if e, ok := err.(*v2error.Error); ok {
73 if e.ErrorCode == v2error.EcodeNodeExist {
74 s.ensuredOnce = true
75 return nil
76 }
77 }
78 return err
79 }
80 s.ensuredOnce = true
81 return nil
82}
83
84func (s *store) enableAuth() error {
85 _, err := s.updateResource("/enabled", true)
86 return err
87}
88func (s *store) disableAuth() error {
89 _, err := s.updateResource("/enabled", false)
90 return err
91}
92
93func (s *store) detectAuth() bool {
94 if s.server == nil {
95 return false
96 }
97 value, err := s.requestResource("/enabled", false)
98 if err != nil {
99 if e, ok := err.(*v2error.Error); ok {
100 if e.ErrorCode == v2error.EcodeKeyNotFound {
101 return false
102 }
103 }
104 if s.lg != nil {
105 s.lg.Warn(
106 "failed to detect auth settings",
107 zap.Error(err),
108 )
109 } else {
110 plog.Errorf("failed to detect auth settings (%s)", err)
111 }
112 return false
113 }
114
115 var u bool
116 err = json.Unmarshal([]byte(*value.Event.Node.Value), &u)
117 if err != nil {
118 if s.lg != nil {
119 s.lg.Warn(
120 "internal bookkeeping value for enabled isn't valid JSON",
121 zap.Error(err),
122 )
123 } else {
124 plog.Errorf("internal bookkeeping value for enabled isn't valid JSON (%v)", err)
125 }
126 return false
127 }
128 return u
129}
130
131func (s *store) requestResource(res string, quorum bool) (etcdserver.Response, error) {
132 ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
133 defer cancel()
134 p := path.Join(StorePermsPrefix, res)
135 method := "GET"
136 if quorum {
137 method = "QGET"
138 }
139 rr := etcdserverpb.Request{
140 Method: method,
141 Path: p,
142 Dir: false, // TODO: always false?
143 }
144 return s.server.Do(ctx, rr)
145}
146
147func (s *store) updateResource(res string, value interface{}) (etcdserver.Response, error) {
148 return s.setResource(res, value, true)
149}
150func (s *store) createResource(res string, value interface{}) (etcdserver.Response, error) {
151 return s.setResource(res, value, false)
152}
153func (s *store) setResource(res string, value interface{}, prevexist bool) (etcdserver.Response, error) {
154 err := s.ensureAuthDirectories()
155 if err != nil {
156 return etcdserver.Response{}, err
157 }
158 ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
159 defer cancel()
160 data, err := json.Marshal(value)
161 if err != nil {
162 return etcdserver.Response{}, err
163 }
164 p := path.Join(StorePermsPrefix, res)
165 rr := etcdserverpb.Request{
166 Method: "PUT",
167 Path: p,
168 Val: string(data),
169 PrevExist: &prevexist,
170 }
171 return s.server.Do(ctx, rr)
172}
173
174func (s *store) deleteResource(res string) error {
175 err := s.ensureAuthDirectories()
176 if err != nil {
177 return err
178 }
179 ctx, cancel := context.WithTimeout(context.Background(), s.timeout)
180 defer cancel()
181 pex := true
182 p := path.Join(StorePermsPrefix, res)
183 _, err = s.server.Do(ctx, etcdserverpb.Request{
184 Method: "DELETE",
185 Path: p,
186 PrevExist: &pex,
187 })
188 return err
189}