1 /* -*- Mode: C++; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- */
2 /*
3  *     Copyright 2017 Couchbase, Inc
4  *
5  *   Licensed under the Apache License, Version 2.0 (the "License");
6  *   you may not use this file except in compliance with the License.
7  *   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 
18 #pragma once
19 
20 #include "collections/manifest.h"
21 
22 #include <boost/optional/optional.hpp>
23 
24 #include <string>
25 #include <vector>
26 
27 namespace Collections {
28 
29 /**
30  * Collections::Filter is an object which will be owned by a DcpProducer. The
31  * Collections::Filter goal is to ultimately enable the DcpProducer to create
32  * ActiveStream filters (Collections::VB::Filters) which do the real job of
33  * dropping keys.
34  *
35  * A Collections::Filter can be constructed so that it is either:
36  *  1. A "legacy filter" ensuring DCP streams act like they always did, in this
37  *     case only items of the default collection are allowed and any collection
38  *     items and SystemEvents are dropped.
39  *  2. A "pass-through filter", which means nothing is filtered, allowing for
40  *     example full vbucket replication to occur, i.e. every item and event of
41  *     every collection is allowed.
42  *  3. A "filter", only items and events of named collections are allowed.
43  *
44  * Because DCP construction is a two-step operation, first create DcpProducer
45  * second create stream(s), and between those two steps collections could be
46  * deleted, the stream cannot police the producer's filter contents as a
47  * collection mis-match due to legitimate deletion cannot be distinguished from
48  * junk-input. Hence the construction of the Collections::Filter performs
49  * validation that the object is sensible with respect to the bucket's
50  * collections configuration. For example, a legacy filter should not be created
51  * if the bucket has removed the $default collection. The same logic applies to
52  * a proper filter, all named collections must be known at the time of creation.
53  *
54  * When creating a "filter" two flavours of JSON input are valid:
55  * Streaming from zero (i.e. you don't know the UID)
56  *   {"collections" : ["name1", "name2", ...]}
57  *
58  * Streaming from non-zero seqno (you need the correct uid):
59  *   {"collections" : [{"name":"name1", "uid":"xxx"},
60  *                     {"name":"name2", "uid":"yyy"}, ...]}
61  *
62  */
63 class Filter {
64 public:
65     /// a name with an optional UID
66     using container =
67             std::vector<std::pair<std::string, boost::optional<uid_t>>>;
68 
69     enum class Type {
70         Name, // Filter is name-only
71         NameUid // Filter contains name:uid entries
72     };
73 
74     /**
75      * Construct a Collections::Filter using an optional JSON document
76      * and the bucket's current Manifest.
77      *
78      * The optional JSON document allows a client to filter a chosen set of
79      * collections.
80      *
81      * if jsonFilter is defined and empty - then we create a passthrough.
82      * if jsonFilter is defined and !empty - then we filter as requested.
83      * if jsonFilter is not defined (maybe a legacy client who doesn't
84      *   understand collections) then only documents with
85      *   DocNamespace::DefaultCollection are allowed.
86      *
87      * @params jsonFilter an optional string as described above.
88      * @params manifest pointer to the current manifest, can be null if no
89      *         manifest has been set.
90      * @throws invalid_argument if the JSON is invalid or contains unknown
91      *         collections.
92      */
93     Filter(boost::optional<const std::string&> jsonFilter,
94            const Manifest* manifest);
95 
96     /**
97      * Get the list of collections the filter knows about. Can be empty
98      * @returns std::vector of std::string, maybe empty for a passthrough filter
99      */
getFilter() const100     const container& getFilter() const {
101         return filter;
102     }
103 
104     /**
105      * @returns if the filter configured so that it allows everything through?
106      */
isPassthrough() const107     bool isPassthrough() const {
108         return passthrough;
109     }
110 
111     /**
112      * @returns if the filter contains the default collection
113      */
allowDefaultCollection() const114     bool allowDefaultCollection() const {
115         return defaultAllowed;
116     }
117 
118     /**
119      * @returns if the filter should allow system events
120      */
allowSystemEvents() const121     bool allowSystemEvents() const {
122         return systemEventsAllowed;
123     }
124 
125     /**
126      * @return the Type the Filter was created from
127      */
getType() const128     Type getType() const {
129         return type;
130     }
131 
132     /**
133      * @return true if the Filter uses name only entries
134      */
isNameFilter() const135     bool isNameFilter() const {
136         return getType() == Type::Name;
137     }
138 
139     /**
140      * @return true if the Filter uses name only entries
141      */
isNameUidFilter() const142     bool isNameUidFilter() const {
143         return getType() == Type::NameUid;
144     }
145 
146     /**
147      * Dump this to std::cerr
148      */
149     void dump() const;
150 
151 private:
152     /**
153      * Private helper to examine the given collection name against the manifest
154      * and add to internal container or throw an exception
155      */
156     void addCollection(const char* collection, const Manifest& manifest);
157 
158     /**
159      * Private helper to examine the given collection object against the
160      * manifest and add to internal container or throw an exception
161      */
162     void addCollection(cJSON* object, const Manifest& manifest);
163 
164     /// A container of named collections to allow, can be empty.
165     container filter;
166 
167     /// Are default collection items allowed?
168     bool defaultAllowed;
169     /// Should everything be allowed (i.e. ignore the vector of names)
170     bool passthrough;
171     /// Should system events be allowed?
172     bool systemEventsAllowed;
173 
174     /// The type of filter that was configured
175     Type type;
176 
177     friend std::ostream& operator<<(std::ostream& os, const Filter& filter);
178 };
179 
180 std::ostream& operator<<(std::ostream& os, const Filter& filter);
181 
182 } // end namespace Collections
183