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
27namespace 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 */
63class Filter {
64public:
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     */
100    const container& getFilter() const {
101        return filter;
102    }
103
104    /**
105     * @returns if the filter configured so that it allows everything through?
106     */
107    bool isPassthrough() const {
108        return passthrough;
109    }
110
111    /**
112     * @returns if the filter contains the default collection
113     */
114    bool allowDefaultCollection() const {
115        return defaultAllowed;
116    }
117
118    /**
119     * @returns if the filter should allow system events
120     */
121    bool allowSystemEvents() const {
122        return systemEventsAllowed;
123    }
124
125    /**
126     * @return the Type the Filter was created from
127     */
128    Type getType() const {
129        return type;
130    }
131
132    /**
133     * @return true if the Filter uses name only entries
134     */
135    bool isNameFilter() const {
136        return getType() == Type::Name;
137    }
138
139    /**
140     * @return true if the Filter uses name only entries
141     */
142    bool isNameUidFilter() const {
143        return getType() == Type::NameUid;
144    }
145
146    /**
147     * Dump this to std::cerr
148     */
149    void dump() const;
150
151private:
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
180std::ostream& operator<<(std::ostream& os, const Filter& filter);
181
182} // end namespace Collections
183