summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul J. Davis <paul.joseph.davis@gmail.com>2019-03-26 16:09:28 -0500
committerPaul J. Davis <paul.joseph.davis@gmail.com>2019-03-26 16:09:28 -0500
commit628795ab535e05531fd35db29564979284184b2f (patch)
tree91d335f6eece40ac9818ee96cd19d00aaf11785d
parentf6e64ec5fceb4b4f2abd60ae5c4e9a8451eddd85 (diff)
downloadcouchdb-628795ab535e05531fd35db29564979284184b2f.tar.gz
Fix compiler errors
-rw-r--r--src/fabric/include/fabric.hrl4
-rw-r--r--src/fabric/src/fabric2.erl116
-rw-r--r--src/fabric/src/fabric2.hrl (renamed from src/fabric/include/fabric2.hrl)11
-rw-r--r--src/fabric/src/fabric2_db.erl173
-rw-r--r--src/fabric/src/fabric2_fdb.erl34
-rw-r--r--src/fabric/src/fabric2_security.erl142
-rw-r--r--src/fabric/src/fabric2_server.erl8
-rw-r--r--src/fabric/src/fabric2_util.erl38
8 files changed, 202 insertions, 324 deletions
diff --git a/src/fabric/include/fabric.hrl b/src/fabric/include/fabric.hrl
index 202304a2f..729253882 100644
--- a/src/fabric/include/fabric.hrl
+++ b/src/fabric/include/fabric.hrl
@@ -13,10 +13,6 @@
-include_lib("eunit/include/eunit.hrl").
--define(uint2bin(I), binary:encode_unsigned(I, little)).
--define(bin2uint(I), binary:decode_unsigned(I, little)).
-
-
-record(collector, {
db_name=nil,
query_args,
diff --git a/src/fabric/src/fabric2.erl b/src/fabric/src/fabric2.erl
deleted file mode 100644
index 6acf23f48..000000000
--- a/src/fabric/src/fabric2.erl
+++ /dev/null
@@ -1,116 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(fabric2).
-
-
-
-
-
-
-
-
-
-
-open_doc(Db, DocId, Options) ->
- fabric2_db:with_tx(Db, fun(TxDb) ->
- case fabric2_doc:get_fdi(TxDb, DocId) of
- not_found ->
- {not_found, missing};
- #full_doc_info{} = FDI ->
- {_, Path} = couch_doc:to_doc_info_path(FDI),
- case fabric2_doc:open(TxDb, DocId, Path) of
- #doc{} = Doc -> {ok, Doc};
- Error -> Error
- end
- end
- end).
-
-
-open_revs(Db, DocId, Revs, Options) ->
- fabric2_db:with_tx(Db, fun(TxDb) ->
- case fabrci2_doc:get_fdi(TxDb, DocId) of
- not_found ->
- {not_found, missing};
- #full_doc_info{} = FDI ->
- case fabric2_doc:open_revs(TxDb, FDI, Revs, Options) of
- [_ | _] = Opened -> {ok, Opened};
- Error -> Error
- end
- end
- end).
-
-
-update_doc(Db, Doc, Options) ->
- fabric2_db:with_tx(Db, fun(TxDb) ->
- case fabric2_doc:update(TxDb, Doc, opts(Options)) of
- {ok, []} ->
- % replication no-op
- #doc{revs = {Pos, [RevId | _]}} = doc(Db, Doc),
- {ok, {Pos, RevId}};
- {ok, NewRev} ->
- {ok, NewRev};
- {error, Error} ->
- throw(Error)
- end
- end).
-
-
-update_docs(DbName, Docs, Options) when is_binary(DbName) ->
- update_docs(open_db(DbName, Options), Docs, Options);
-
-update_docs(Db, Docs, Options) ->
- fabric2_db:with_tx(Db, fun(TxDb) ->
- {Resps, Status} = lists:mapfoldl(fun(Doc, Acc) ->
- case fabric2_doc:update(TxDb, Doc, opts(Options)) of
- {ok, _} = Resp ->
- {Resp, Acc};
- {error, _} = Resp ->
- {Resp, error}
- end
- end, ok, Docs),
- {Status, Resps}
- end).
-
-
-docs(Db, Docs) ->
- lists:map(fun(Doc) -> doc(Db, Doc) end, Docs).
-
-
-doc(_Db, #doc{} = Doc) ->
- Doc;
-
-doc(Db, {_} = Doc) ->
- couch_db:doc_from_json_obj_validate(Db, Doc);
-
-doc(_Db, Doc) ->
- erlang:error({illegal_doc_format, Doc}).
-
-
-opts(Options) ->
- lists:foldl(fun(Opt, Acc) ->
- add_option(Opt, Acc)
- end, Options, [user_ctx, io_priority]).
-
-
-add_option(Key, Options) ->
- case couch_util:get_value(Key, Options) of
- undefined ->
- case erlang:get(Key) of
- undefined ->
- Options;
- Value ->
- [{Key, Value} | Options]
- end;
- _ ->
- Options
- end.
diff --git a/src/fabric/include/fabric2.hrl b/src/fabric/src/fabric2.hrl
index e56f84fdc..d63d35064 100644
--- a/src/fabric/include/fabric2.hrl
+++ b/src/fabric/src/fabric2.hrl
@@ -11,11 +11,6 @@
% the License.
--define(SYSTEM_DATABASES, [
- <<"_dbs">>,
- <<"_global_changes">>,
- <<"_metadata">>,
- <<"_nodes">>,
- <<"_replicator">>,
- <<"_users">>
-]).
+-define(uint2bin(I), binary:encode_unsigned(I, little)).
+-define(bin2uint(I), binary:decode_unsigned(I, little)).
+
diff --git a/src/fabric/src/fabric2_db.erl b/src/fabric/src/fabric2_db.erl
index 5df51dde7..d172209bd 100644
--- a/src/fabric/src/fabric2_db.erl
+++ b/src/fabric/src/fabric2_db.erl
@@ -18,6 +18,10 @@
open/2,
delete/2,
+ is_admin/1,
+ check_is_admin/1,
+ check_is_member/1,
+
name/1,
get_after_doc_read_fun/1,
get_before_doc_update_fun/1,
@@ -119,6 +123,7 @@
]).
+-include_lib("couch/include/couch_db.hrl").
-include_lib("fabric/include/fabric2.hrl").
@@ -132,13 +137,13 @@
create(DbName, Options) ->
- Result = fabric2_util:transactional(DbName, Options, fun(TxDb) ->
+ Result = transactional(DbName, Options, fun(TxDb) ->
case fabric2_fdb:db_exists(TxDb) of
true ->
{error, file_exists};
false ->
fabric2_fdb:db_create(TxDb)
- end,
+ end
end),
% We cache outside of the transaction so that we're sure
% that this request created the database
@@ -153,17 +158,17 @@ create(DbName, Options) ->
open(DbName, Options) ->
case fabric2_server:fetch(DbName) of
#{} = Db ->
- fabric2_util:transactional(Db, fun(TxDb) ->
+ with_tx(Db, fun(TxDb) ->
case fabric2_fdb:db_is_current(TxDb) of
true ->
Db;
false ->
- Reopend = fabric2_fdb:db_open(TxDb),
+ Reopened = fabric2_fdb:db_open(TxDb),
fabric2_server:store(Reopened)
end
end);
undefined ->
- fabric2_util:transactional(DbName, Options, fun(TxDb) ->
+ transactional(DbName, Options, fun(TxDb) ->
Opened = fabric2_fdb:db_open(TxDb),
fabric2_server:store(Opened)
end)
@@ -178,6 +183,34 @@ delete(DbName, Options) ->
end).
+is_admin(Db) ->
+ % TODO: Need to re-consider couch_db_plugin:check_is_admin/1
+ {SecProps} = get_security(Db),
+ UserCtx = get_user_ctx(Db),
+ {Admins} = get_admins(SecProps),
+ is_authorized(Admins, UserCtx).
+
+
+check_is_admin(Db) ->
+ case is_admin(Db) of
+ true ->
+ ok;
+ false ->
+ UserCtx = get_user_ctx(Db),
+ Reason = <<"You are not a db or server admin.">>,
+ throw_security_error(UserCtx, Reason)
+ end.
+
+
+check_is_member(Db) ->
+ case is_member(Db) of
+ true ->
+ ok;
+ false ->
+ UserCtx = get_user_ctx(Db),
+ throw_security_error(UserCtx)
+ end.
+
name(#{name := DbName}) ->
DbName.
@@ -190,7 +223,7 @@ get_after_doc_read_fun(#{after_doc_read := AfterDocRead}) ->
get_before_doc_update_fun(#{before_doc_update := BeforeDocUpdate}) ->
BeforeDocUpdate.
-get_commited_update_seq(#{} = Db) ->
+get_committed_update_seq(#{} = Db) ->
get_update_seq(Db).
@@ -198,7 +231,7 @@ get_compacted_seq(#{} = Db) ->
get_update_seq(Db).
-get_compactor_pid(#{} = Db) ->
+get_compactor_pid(#{} = _Db) ->
nil.
@@ -211,7 +244,7 @@ get_db_info(#{} = Db) ->
{cluster, {[{n, 0}, {q, 0}, {r, 0}, {w, 0}]}},
{compact_running, false},
{data_size, 0},
- {db_name, DbName},
+ {db_name, name(Db)},
{disk_format_version, 0},
{disk_size, 0},
{instance_start_time, <<"0">>},
@@ -246,7 +279,7 @@ get_doc_count(Db, Key) ->
end).
-get_instance_startime(#{}) ->
+get_instance_start_time(#{}) ->
0.
@@ -271,6 +304,10 @@ get_update_seq(#{} = Db) ->
end.
+get_user_ctx(#{user_ctx := UserCtx}) ->
+ UserCtx.
+
+
get_uuid(#{uuid := UUID}) ->
UUID.
@@ -336,7 +373,7 @@ open_doc(#{} = Db, DocId) ->
open_doc(Db, DocId, []).
-open_doc(#{} = Db, DocId, Options) ->
+open_doc(#{} = Db, DocId, _Options) ->
with_tx(Db, fun(TxDb) ->
case fabric2_fdb:get_full_doc_info(TxDb, DocId) of
not_found ->
@@ -372,7 +409,7 @@ open_doc_revs(Db, FDI, Revs, Options) ->
% We have the rev in our list but know nothing about it
{{not_found, missing}, {Pos, Rev}};
_ ->
- case fabric2_fdb:get_doc_body(Db, Id, RevPath) of
+ case fabric2_fdb:get_doc_body(TxDb, Id, RevPath) of
#doc{} = Doc -> {ok, Doc};
Else -> {Else, {Pos, Rev}}
end
@@ -400,7 +437,7 @@ update_docs(Db, Docs) ->
update_docs(Db, Docs, Options) ->
with_tx(Db, fun(TxDb) ->
{Resps, Status} = lists:mapfoldl(fun(Doc, Acc) ->
- case fabric2_doc:update(TxDb, Doc, opts(Options)) of
+ case fabric2_doc:update(TxDb, Doc, Options) of
{ok, _} = Resp ->
{Resp, Acc};
{error, _} = Resp ->
@@ -440,6 +477,92 @@ new_revid(Doc) ->
Doc#doc{revs = {OldStart + 1, [Rev | OldRevs]}}.
+is_member(Db) ->
+ {SecProps} = get_security(Db),
+ case is_admin(Db) of
+ true ->
+ true;
+ false ->
+ case is_public_db(SecProps) of
+ true ->
+ true;
+ false ->
+ {Members} = get_members(SecProps),
+ UserCtx = get_user_ctx(Db),
+ is_authorized(Members, UserCtx)
+ end
+ end.
+
+
+is_authorized(Group, UserCtx) ->
+ #user_ctx{
+ name = UserName,
+ roles = UserRoles
+ } = UserCtx,
+ Names = fabric2_util:get_value(<<"names">>, Group, []),
+ Roles = fabric2_util:get_value(<<"roles">>, Group, []),
+ case check_security(roles, UserRoles, [<<"_admin">> | Roles]) of
+ true ->
+ true;
+ false ->
+ check_security(names, UserName, Names)
+ end.
+
+
+check_security(roles, [], _) ->
+ false;
+check_security(roles, UserRoles, Roles) ->
+ UserRolesSet = ordsets:from_list(UserRoles),
+ RolesSet = ordsets:from_list(Roles),
+ not ordsets:is_disjoint(UserRolesSet, RolesSet);
+check_security(names, _, []) ->
+ false;
+check_security(names, null, _) ->
+ false;
+check_security(names, UserName, Names) ->
+ lists:member(UserName, Names).
+
+
+throw_security_error(#user_ctx{name = null} = UserCtx) ->
+ Reason = <<"You are not authorized to access this db.">>,
+ throw_security_error(UserCtx, Reason);
+throw_security_error(#user_ctx{name = _} = UserCtx) ->
+ Reason = <<"You are not allowed to access this db.">>,
+ throw_security_error(UserCtx, Reason).
+
+
+throw_security_error(#user_ctx{} = UserCtx, Reason) ->
+ Error = security_error_type(UserCtx),
+ throw({Error, Reason}).
+
+
+security_error_type(#user_ctx{name = null}) ->
+ unauthorized;
+security_error_type(#user_ctx{name = _}) ->
+ forbidden.
+
+
+is_public_db(SecProps) ->
+ {Members} = get_members(SecProps),
+ Names = fabric2_util:get_value(<<"names">>, Members, []),
+ Roles = fabric2_util:get_value(<<"roles">>, Members, []),
+ Names =:= [] andalso Roles =:= [].
+
+
+get_admins(SecProps) ->
+ fabric2_util:get_value(<<"admins">>, SecProps, {[]}).
+
+
+get_members(SecProps) ->
+ % we fallback to readers here for backwards compatibility
+ case fabric2_util:get_value(<<"members">>, SecProps) of
+ undefined ->
+ fabric2_util:get_value(<<"readers">>, SecProps, {[]});
+ Members ->
+ Members
+ end.
+
+
% TODO: Handle _local docs separately.
update_doc_int(#{} = Db, #doc{} = Doc0, Options) ->
UpdateType = case lists:member(replicated_changes, Options) of
@@ -449,7 +572,7 @@ update_doc_int(#{} = Db, #doc{} = Doc0, Options) ->
try
FDI1 = fabric2_fdb:get_full_doc_info(Db, Doc0#doc.id),
- Doc1 = prep_and_validate(TxDb, FDI1, Doc0, UpdateType),
+ Doc1 = prep_and_validate(Db, FDI1, Doc0, UpdateType),
Doc2 = case UpdateType of
interactive_edit -> new_revid(Doc1);
replicated_changes -> Doc1
@@ -466,7 +589,7 @@ update_doc_int(#{} = Db, #doc{} = Doc0, Options) ->
end,
NewExists = not FDI3#full_doc_info.deleted,
- ok = fabric2_fdb:write_doc(Db, FDI3, Doc3)
+ ok = fabric2_fdb:write_doc(Db, FDI3, Doc3),
case {OldExists, NewExists} of
{false, true} ->
@@ -482,6 +605,9 @@ update_doc_int(#{} = Db, #doc{} = Doc0, Options) ->
% Need to count design documents
% Need to track db size changes
+ #doc{
+ revs = {RevStart, [Rev | _]}
+ } = Doc3,
{ok, {RevStart, Rev}}
catch throw:{?MODULE, Return} ->
Return
@@ -526,9 +652,9 @@ prep_and_validate(Db, FDI, Doc, interactive_edit) ->
?RETURN({error, conflict})
end
end,
- prep_and_validate(TxDb, Doc, GetDocFun);
+ prep_and_validate(Db, Doc, GetDocFun);
-prep_and_validate(TxDb, FDI, Doc, replicated_changes) ->
+prep_and_validate(Db, FDI, Doc, replicated_changes) ->
#full_doc_info{
rev_tree = RevTree
} = FDI,
@@ -570,7 +696,7 @@ prep_and_validate(TxDb, FDI, Doc, replicated_changes) ->
?RETURN({ok, []})
end,
- prep_and_validate(TxDb, Doc, GetDocFun).
+ prep_and_validate(Db, Doc, GetDocFun).
prep_and_validate(Db, Doc, GetDocBody) ->
@@ -606,7 +732,7 @@ merge_rev_tree(FDI, Doc, interactive_edit) when FDI#full_doc_info.deleted ->
% Update the new doc based on revisions in OldInfo
#doc_info{revs=[WinningRev | _]} = couch_doc:to_doc_info(FDI),
#rev_info{rev={OldPos, OldRev}} = WinningRev,
- Body = case couch_util:get_value(comp_body, Doc#doc.meta) of
+ Body = case fabric2_util:get_value(comp_body, Doc#doc.meta) of
CompBody when is_binary(CompBody) ->
couch_compress:decompress(CompBody);
_ ->
@@ -676,9 +802,6 @@ merge_rev_tree(FDI, Doc, replicated_changes) ->
validate_doc_update(Db, #doc{id = <<"_design/", _/binary>>} = Doc, _) ->
- #{
- security_doc := Security
- } = Db,
case catch check_is_admin(Db) of
ok -> validate_ddoc(Db, Doc);
Error -> ?RETURN(Error)
@@ -693,7 +816,7 @@ validate_doc_update(Db, Doc, GetDiskDocFun) ->
} = Db,
Fun = fun() ->
DiskDoc = GetDiskDocFun(),
- JsonCtx = fabric2_util:user_ctx_to_json(TxDb),
+ JsonCtx = fabric2_util:user_ctx_to_json(UserCtx),
try
lists:map(fun(VDU) ->
case VDU(Doc, DiskDoc, JsonCtx, Security) of
@@ -742,6 +865,12 @@ find_prev_known_rev(Pos, [{_Rev, #leaf{}} | _] = DocPath) ->
{Pos, [Rev || {Rev, _Val} <- DocPath]}.
+transactional(DbName, Options, Fun) ->
+ fabric2_util:transactional(fun(Tx) ->
+ Fun(fabric2_fdb:init(Tx, DbName, Options))
+ end).
+
+
with_tx(#{tx := undefined} = Db, Fun) ->
fabric2_util:transactional(fun(Tx) ->
Fun(Db#{tx => Tx})
diff --git a/src/fabric/src/fabric2_fdb.erl b/src/fabric/src/fabric2_fdb.erl
index 4764331ae..e5bd81a63 100644
--- a/src/fabric/src/fabric2_fdb.erl
+++ b/src/fabric/src/fabric2_fdb.erl
@@ -38,6 +38,10 @@
]).
+-include("couch/include/couch_db.hrl").
+-include("fabric2.hrl").
+
+
% This will eventually be the `\xFFmetadataVersion` key that is
% currently only available in FoundationDB master.
%
@@ -55,18 +59,20 @@
-define(CLUSTER_CONFIG, 0).
-define(ALL_DBS, 1).
-define(DBS, 15).
+
-define(DB_CONFIG, 16).
-define(DB_STATS, 17).
-define(DB_ALL_DOCS, 18).
-define(DB_CHANGES, 19).
--define(DB_DOCS, 20).
--define(DB_REVS, 21).
+-define(DB_REVS, 20).
+-define(DB_DOCS, 21).
+-define(DB_LOCAL_DOCS, 22).
% Various utility macros
-define(REQUIRE_TX(Db), {erlfdb_transaction, _} = maps:get(Db, tx)).
--define(REQUIRE_CURRENT(Db), true = db_is_current(Db)).
+-define(REQUIRE_CURRENT(Db), true = is_current(Db)).
-define(UNSET_VS, {versionstamp, 16#FFFFFFFFFFFFFFFF, 16#FFFF}).
@@ -95,7 +101,7 @@ create(#{} = Db) ->
% we're just using the DbName so that debugging is easier.
DbKey = erlfdb_tuple:pack({?ALL_DBS, DbName}, LayerPrefix),
DbPrefix = erlfdb_tuple:pack({?DBS, DbName}, LayerPrefix),
- ets:set(Tx, DbKey, DbPrefix),
+ erlfdb:set(Tx, DbKey, DbPrefix),
UUID = fabric2_util:uuid(),
@@ -121,7 +127,7 @@ create(#{} = Db) ->
db_prefix => DbPrefix,
version => Version,
revs_limit => 1000,
- security_doc => {[]}
+ security_doc => {[]},
validate_doc_update_funs => [],
user_ctx => #user_ctx{},
@@ -161,7 +167,6 @@ open(#{} = Db0) ->
after_doc_read => undefined
},
- Config = db_get_config(Db1),
lists:foldl(fun({Key, Val}) ->
case Key of
<<"uuid">> ->
@@ -171,7 +176,7 @@ open(#{} = Db0) ->
<<"security_doc">> ->
Db1#{security_doc => ?JSON_DECODE(Val)}
end
- end, Db1, db_get_config(Db1)).
+ end, Db1, get_config(Db1)).
delete(#{} = Db) ->
@@ -186,7 +191,7 @@ delete(#{} = Db) ->
DbKey = erlfdb_tuple:pack({?DBS, DbName}, LayerPrefix),
erlfdb:clear(Tx, DbKey),
erlfdb:clear_range_startswith(Tx, DbPrefix),
- bump_metadata_version(),
+ bump_metadata_version(Db),
ok.
@@ -207,7 +212,7 @@ exists(#{name := DbName} = Db) when is_binary(DbName) ->
is_current(#{} = Db) ->
?REQUIRE_TX(Db),
#{
- name := DbName,
+ tx := Tx,
md_version := MetaDataVersion
} = Db,
@@ -220,7 +225,6 @@ is_current(#{} = Db) ->
get_info(#{} = Db) ->
?REQUIRE_CURRENT(Db),
#{
- name := DbName,
tx := Tx,
db_prefix := DbPrefix
} = Db,
@@ -361,10 +365,6 @@ store_doc(#{} = Db, #full_doc_info{} = FDI, #doc{} = Doc) ->
update_seq = OldUpdateSeq
} = FDI,
- #doc{
- revs = {RevStart, [Rev | _]}
- } = Doc,
-
% Delete old entry in changes feed
OldSeqKey = erlfdb_tuple:pack({?DB_CHANGES, OldUpdateSeq}, DbPrefix),
erlfdb:clear(Tx, OldSeqKey),
@@ -378,7 +378,7 @@ store_doc(#{} = Db, #full_doc_info{} = FDI, #doc{} = Doc) ->
erlfdb:set(Tx, NewDocKey, NewDocVal),
% Update revision tree entry
- {NewFDIKey, NewFDIVal} = fdi_to_fdb(TxDb, FDI),
+ {NewFDIKey, NewFDIVal} = fdi_to_fdb(Db, FDI),
erlfdb:set_versionstamped_value(Tx, NewFDIKey, NewFDIVal).
@@ -453,7 +453,7 @@ fdi_to_fdb(Db, #full_doc_info{} = FDI) ->
ValTuple = {
Deleted,
RevTreeBin,
- {versionstamp, 16#FFFFFFFFFFFFFFFF, 16#FFFF}
+ ?UNSET_VS
},
Val = erlfdb_tuple:pack_vs(ValTuple),
{Key, Val}.
@@ -487,4 +487,4 @@ fdb_to_fdi(_Db, Id, Bin) when is_binary(Bin) ->
update_seq = UpdateSeq
};
fdb_to_fdi(_Db, _Id, not_found) ->
- not_found. \ No newline at end of file
+ not_found.
diff --git a/src/fabric/src/fabric2_security.erl b/src/fabric/src/fabric2_security.erl
deleted file mode 100644
index b3f13886d..000000000
--- a/src/fabric/src/fabric2_security.erl
+++ /dev/null
@@ -1,142 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(fabric_security).
-
-
--export([
- check_is_admin/2,
- check_is_member/2,
-
- is_admin/2,
- is_member/2
-]).
-
-
--include_lib("couch/include/couch_db.hrl").
-
-
-check_is_admin(DbName, UserCtx) ->
- case is_admin(DbName, UserCtx) of
- true ->
- ok;
- false ->
- Reason = <<"You are not a db or server admin.">>,
- throw_security_error(UserCtx, Reason)
- end.
-
-
-check_is_member(DbName, UserCtx) ->
- case is_member(DbName, UserCtx) of
- true ->
- ok;
- false ->
- throw_security_error(UserCtx)
- end.
-
-
-is_admin(DbName, UserCtx) when is_binary(DbName) ->
- {SecProps} = fabric2:get_security(DbName),
- is_admin(SecProps, UserCtx);
-
-is_admin(SecProps, UserCtx) ->
- % Need to re-consider couch_db_plugin:check_is_admin/1
- {Admins} = get_admins(SecProps),
- is_authorized(Admins, UserCtx).
-
-
-is_member(DbName, UserCtx) when is_binary(DbName) ->
- {SecProps} = fabric2:get_security(DbName),
- is_member(SecProps, UserCtx);
-
-is_member(SecProps, UserCtx) ->
- case is_admin(SecProps, UserCtx) of
- true ->
- true;
- false ->
- case is_public_db(SecProps) of
- true ->
- true;
- false ->
- {Members} = get_members(SecProps),
- is_authorized(Members, UserCtx)
- end
- end.
-
-
-is_authorized(Group, UserCtx) ->
- #user_ctx{
- name = UserName,
- roles = UserRoles
- } = UserCtx,
- Names = couch_util:get_value(<<"names">>, Group, []),
- Roles = couch_util:get_value(<<"roles">>, Group, []),
- case check_security(roles, UserRoles, [<<"_admin">> | Roles]) of
- true ->
- true;
- false ->
- check_security(names, UserName, Names)
- end.
-
-
-check_security(roles, [], _) ->
- false;
-check_security(roles, UserRoles, Roles) ->
- UserRolesSet = ordsets:from_list(UserRoles),
- RolesSet = ordsets:from_list(Roles),
- not ordsets:is_disjoint(UserRolesSet, RolesSet);
-check_security(names, _, []) ->
- false;
-check_security(names, null, _) ->
- false;
-check_security(names, UserName, Names) ->
- lists:member(UserName, Names).
-
-
-throw_security_error(#user_ctx{name=null}=UserCtx) ->
- Reason = <<"You are not authorized to access this db.">>,
- throw_security_error(UserCtx, Reason);
-throw_security_error(#user_ctx{name=_}=UserCtx) ->
- Reason = <<"You are not allowed to access this db.">>,
- throw_security_error(UserCtx, Reason).
-
-
-throw_security_error(#user_ctx{}=UserCtx, Reason) ->
- Error = security_error_type(UserCtx),
- throw({Error, Reason}).
-
-
-security_error_type(#user_ctx{name=null}) ->
- unauthorized;
-security_error_type(#user_ctx{name=_}) ->
- forbidden.
-
-
-is_public_db(SecProps) ->
- {Members} = get_members(SecProps),
- Names = couch_util:get_value(<<"names">>, Members, []),
- Roles = couch_util:get_value(<<"roles">>, Members, []),
- Names =:= [] andalso Roles =:= [].
-
-
-get_admins(SecProps) ->
- couch_util:get_value(<<"admins">>, SecProps, {[]}).
-
-
-get_members(SecProps) ->
- % we fallback to readers here for backwards compatibility
- case couch_util:get_value(<<"members">>, SecProps) of
- undefined ->
- couch_util:get_value(<<"readers">>, SecProps, {[]});
- Members ->
- Members
- end.
diff --git a/src/fabric/src/fabric2_server.erl b/src/fabric/src/fabric2_server.erl
index 58223315a..9a0e4fac2 100644
--- a/src/fabric/src/fabric2_server.erl
+++ b/src/fabric/src/fabric2_server.erl
@@ -32,6 +32,9 @@
]).
+-include_lib("couch/include/couch_db.hrl").
+
+
-define(CLUSTER_FILE, "/usr/local/etc/foundationdb/fdb.cluster").
@@ -41,7 +44,7 @@ start_link() ->
fetch(DbName) when is_binary(DbName) ->
case ets:lookup(?MODULE, DbName) of
- [{DbName, #{} = Db] -> Db;
+ [{DbName, #{} = Db}] -> Db;
[] -> undefined
end.
@@ -51,7 +54,7 @@ store(#{name := DbName} = Db0) when is_binary(DbName) ->
tx => undefined,
user_ctx => #user_ctx{}
},
- true = ets:insert(?MODULE, {DbName, Db1}}),
+ true = ets:insert(?MODULE, {DbName, Db1}),
Db1.
@@ -66,7 +69,6 @@ init(_) ->
ClusterStr = config:get("erlfdb", "cluster_file", ?CLUSTER_FILE),
Db = erlfdb:open(iolist_to_binary(ClusterStr)),
application:set_env(fabric, db, Db),
- init_cluster(Db),
{ok, nil}.
diff --git a/src/fabric/src/fabric2_util.erl b/src/fabric/src/fabric2_util.erl
index 1c47222f6..230a5841b 100644
--- a/src/fabric/src/fabric2_util.erl
+++ b/src/fabric/src/fabric2_util.erl
@@ -14,26 +14,28 @@
-export([
- transactional/2,
- transactional/3,
-
+ transactional/1,
get_db_handle/0,
user_ctx_to_json/1,
- uuid/0,
-
+ get_value/2,
+ get_value/3,
to_hex/1,
+ uuid/0,
debug_cluster/0,
debug_cluster/2
]).
+-include_lib("couch/include/couch_db.hrl").
+
+
-define(PDICT_DB_KEY, '$erlfdb_handle').
-trasactional(Fun) when is_function(Fun, 1) ->
+transactional(Fun) when is_function(Fun, 1) ->
Db = get_db_handle(),
erlfdb:transactional(Db, Fun).
@@ -53,14 +55,22 @@ user_ctx_to_json(Db) ->
UserCtx = fabric2_db:get_user_ctx(Db),
{[
{<<"db">>, fabric2_db:name(Db)},
- {<<"name">>, Ctx#user_ctx.name},
- {<<"roles">>, Ctx#user_ctx.roles}
+ {<<"name">>, UserCtx#user_ctx.name},
+ {<<"roles">>, UserCtx#user_ctx.roles}
]}.
+get_value(Key, List) ->
+ get_value(Key, List, undefined).
-uuid() ->
- to_hex(crypto:strong_rand_bytes(16)).
+
+get_value(Key, List, Default) ->
+ case lists:keysearch(Key, 1, List) of
+ {value, {Key,Value}} ->
+ Value;
+ false ->
+ Default
+ end.
to_hex(Bin) ->
@@ -70,7 +80,7 @@ to_hex(Bin) ->
to_hex_int(<<>>) ->
[];
to_hex_int(<<Hi:4, Lo:4, Rest/binary>>) ->
- [nibble_to_hex(Hi), nibble_to_hex(Lo) | to_hex(Rest)];
+ [nibble_to_hex(Hi), nibble_to_hex(Lo) | to_hex(Rest)].
nibble_to_hex(I) ->
@@ -94,6 +104,10 @@ nibble_to_hex(I) ->
end.
+uuid() ->
+ to_hex(crypto:strong_rand_bytes(16)).
+
+
debug_cluster() ->
debug_cluster(<<>>, <<16#FE, 16#FF, 16#FF>>).
@@ -106,4 +120,4 @@ debug_cluster(Start, End) ->
erlfdb_util:repr(Val)
])
end, erlfdb:get_range(Tx, Start, End))
- end). \ No newline at end of file
+ end).