1//  Copyright (c) 2016 Couchbase, Inc.
2//  Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file
3//  except in compliance with the License. You may obtain a copy of the License at
4//    http://www.apache.org/licenses/LICENSE-2.0
5//  Unless required by applicable law or agreed to in writing, software distributed under the
6//  License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
7//  either express or implied. See the License for the specific language governing permissions
8//  and limitations under the License.
9
10package system
11
12import (
13	"fmt"
14
15	"github.com/couchbase/query/auth"
16	"github.com/couchbase/query/datastore"
17	"github.com/couchbase/query/errors"
18	"github.com/couchbase/query/expression"
19	"github.com/couchbase/query/timestamp"
20	"github.com/couchbase/query/value"
21)
22
23type userInfoKeyspace struct {
24	keyspaceBase
25	name    string
26	indexer datastore.Indexer
27}
28
29func (b *userInfoKeyspace) Release() {
30}
31
32func (b *userInfoKeyspace) NamespaceId() string {
33	return b.namespace.Id()
34}
35
36func (b *userInfoKeyspace) Id() string {
37	return b.Name()
38}
39
40func (b *userInfoKeyspace) Name() string {
41	return b.name
42}
43
44func getUserInfoList(s *store) ([]interface{}, errors.Error) {
45	val, err := s.UserInfo()
46	if err != nil {
47		return nil, err
48	}
49	// Expected data format:
50	//   [{"id":"ivanivanov","name":"Ivan Ivanov","roles":[{"role":"cluster_admin"},{"bucket_name":"default","role":"bucket_admin"}]},
51	//    {"id":"petrpetrov","name":"Petr Petrov","roles":[{"role":"replication_admin"}]}]
52	data := val.Actual()
53	sliceOfUsers, ok := data.([]interface{})
54	if !ok {
55		return nil, errors.NewInvalidValueError(fmt.Sprintf("Unexpected format for user_info received from server: %v", data))
56	}
57
58	return sliceOfUsers, nil
59}
60
61func (b *userInfoKeyspace) Count(context datastore.QueryContext) (int64, errors.Error) {
62	uil, err := getUserInfoList(b.namespace.store)
63	if err != nil {
64		return 0, err
65	}
66	return int64(len(uil)), nil
67}
68
69func (b *userInfoKeyspace) Indexer(name datastore.IndexType) (datastore.Indexer, errors.Error) {
70	return b.indexer, nil
71}
72
73func (b *userInfoKeyspace) Indexers() ([]datastore.Indexer, errors.Error) {
74	return []datastore.Indexer{b.indexer}, nil
75}
76
77func (b *userInfoKeyspace) Fetch(keys []string, keysMap map[string]value.AnnotatedValue,
78	context datastore.QueryContext, subPaths []string) (errs []errors.Error) {
79	sliceOfUsers, err := getUserInfoList(b.namespace.store)
80	if err != nil {
81		return []errors.Error{err}
82	}
83	newMap, err := userInfoListToMap(sliceOfUsers)
84	if err != nil {
85		return []errors.Error{err}
86	}
87
88	for _, k := range keys {
89		val := newMap[k]
90		if val == nil {
91			continue
92		}
93
94		item := value.NewAnnotatedValue(val)
95		item.SetAttachment("meta", map[string]interface{}{
96			"id": k,
97		})
98		keysMap[k] = item
99	}
100
101	return
102}
103
104func (b *userInfoKeyspace) Insert(inserts []value.Pair) ([]value.Pair, errors.Error) {
105	return nil, errors.NewSystemNotImplementedError(nil, "")
106}
107
108func (b *userInfoKeyspace) Update(updates []value.Pair) ([]value.Pair, errors.Error) {
109	return nil, errors.NewSystemNotImplementedError(nil, "")
110}
111
112func (b *userInfoKeyspace) Upsert(upserts []value.Pair) ([]value.Pair, errors.Error) {
113	return nil, errors.NewSystemNotImplementedError(nil, "")
114}
115
116func (b *userInfoKeyspace) Delete(deletes []string, context datastore.QueryContext) ([]string, errors.Error) {
117	return nil, errors.NewSystemNotImplementedError(nil, "")
118}
119
120func newUserInfoKeyspace(p *namespace) (*userInfoKeyspace, errors.Error) {
121	b := new(userInfoKeyspace)
122	setKeyspaceBase(&b.keyspaceBase, p)
123	b.name = KEYSPACE_NAME_USER_INFO
124
125	primary := &userInfoIndex{name: "#primary", keyspace: b}
126	b.indexer = newSystemIndexer(b, primary)
127	setIndexBase(&primary.indexBase, b.indexer)
128
129	return b, nil
130}
131
132type userInfoIndex struct {
133	indexBase
134	name     string
135	keyspace *userInfoKeyspace
136}
137
138func (pi *userInfoIndex) KeyspaceId() string {
139	return pi.keyspace.Id()
140}
141
142func (pi *userInfoIndex) Id() string {
143	return pi.Name()
144}
145
146func (pi *userInfoIndex) Name() string {
147	return pi.name
148}
149
150func (pi *userInfoIndex) Type() datastore.IndexType {
151	return datastore.SYSTEM
152}
153
154func (pi *userInfoIndex) SeekKey() expression.Expressions {
155	return nil
156}
157
158func (pi *userInfoIndex) RangeKey() expression.Expressions {
159	return nil
160}
161
162func (pi *userInfoIndex) Condition() expression.Expression {
163	return nil
164}
165
166func (pi *userInfoIndex) IsPrimary() bool {
167	return true
168}
169
170func (pi *userInfoIndex) State() (state datastore.IndexState, msg string, err errors.Error) {
171	return datastore.ONLINE, "", nil
172}
173
174func (pi *userInfoIndex) Statistics(requestId string, span *datastore.Span) (
175	datastore.Statistics, errors.Error) {
176	return nil, nil
177}
178
179func (pi *userInfoIndex) Drop(requestId string) errors.Error {
180	return errors.NewSystemIdxNoDropError(nil, "")
181}
182
183func (pi *userInfoIndex) Scan(requestId string, span *datastore.Span, distinct bool, limit int64,
184	cons datastore.ScanConsistency, vector timestamp.Vector, conn *datastore.IndexConnection) {
185
186	pi.ScanEntries(requestId, limit, cons, vector, conn)
187}
188
189func userInfoListToMap(sliceOfUsers []interface{}) (map[string]value.Value, errors.Error) {
190	newMap := make(map[string]value.Value, len(sliceOfUsers))
191	for i, u := range sliceOfUsers {
192		userAsMap, ok := u.(map[string]interface{})
193		if !ok {
194			return nil, errors.NewInvalidValueError(fmt.Sprintf("Unexpected format for user_info at position %d: %v", i, u))
195		}
196		auth.ConvertRolesToAliases(userAsMap)
197		id, present := userAsMap["id"]
198		if !present {
199			return nil, errors.NewInvalidValueError(fmt.Sprintf("Could not find id in user_info data at position %d: %v", i, u))
200		}
201		idAsString, ok := id.(string)
202		if !ok {
203			return nil, errors.NewInvalidValueError(fmt.Sprintf("Field id of unexpected type in user_info data at position %d: %v", i, u))
204		}
205		domain, present := userAsMap["domain"]
206		if !present {
207			return nil, errors.NewInvalidValueError(fmt.Sprintf("Could not find domain in user_info data at position %d: %v", i, u))
208		}
209		domainAsString, ok := domain.(string)
210		if !ok {
211			return nil, errors.NewInvalidValueError(
212				fmt.Sprintf("Field domain of unexpected type in user_info data at position %d: %v", i, u))
213		}
214		userKey := fmt.Sprintf("%s:%s", domainAsString, idAsString)
215		newMap[userKey] = value.NewValue(u)
216	}
217	return newMap, nil
218}
219
220func (pi *userInfoIndex) ScanEntries(requestId string, limit int64, cons datastore.ScanConsistency,
221	vector timestamp.Vector, conn *datastore.IndexConnection) {
222	defer close(conn.EntryChannel())
223
224	sliceOfUsers, err := getUserInfoList(pi.keyspace.namespace.store)
225	if err != nil {
226		conn.Fatal(err)
227		return
228	}
229	mapOfUsers, err := userInfoListToMap(sliceOfUsers)
230	if err != nil {
231		conn.Fatal(err)
232		return
233	}
234
235	var numProduced int64
236	for k, _ := range mapOfUsers {
237		if limit > 0 && numProduced > limit {
238			break
239		}
240
241		entry := datastore.IndexEntry{PrimaryKey: k}
242		if !sendSystemKey(conn, &entry) {
243			return
244		}
245		numProduced++
246	}
247}
248