blob: 1a2d33f790e67116d2c8de808a9b246fd83930de [file] [log] [blame]
% 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(couch_server).
-behaviour(gen_server).
-behaviour(config_listener).
-vsn(3).
-export([get_version/0, get_version/1, get_git_sha/0, get_uuid/0]).
-export([init/1, handle_call/3, sup_start_link/0]).
-export([handle_cast/2, code_change/3, handle_info/2, terminate/2]).
-export([is_admin/2, has_admins/0]).
% config_listener api
-export([handle_config_change/5, handle_config_terminate/3]).
-include_lib("couch/include/couch_db.hrl").
-define(RELISTEN_DELAY, 5000).
get_version() ->
%% Defined in rebar.config.script
?COUCHDB_VERSION.
get_version(short) ->
%% strip git hash from version string
[Version | _Rest] = string:tokens(get_version(), "+"),
Version.
get_git_sha() -> ?COUCHDB_GIT_SHA.
get_uuid() ->
case config:get("couchdb", "uuid", undefined) of
undefined ->
UUID = couch_uuids:random(),
config:set("couchdb", "uuid", ?b2l(UUID)),
UUID;
UUID ->
?l2b(UUID)
end.
sup_start_link() ->
gen_server:start_link({local, couch_server}, couch_server, [], []).
is_admin(User, ClearPwd) ->
case config:get("admins", User) of
"-hashed-" ++ HashedPwdAndSalt ->
[HashedPwd, Salt] = string:tokens(HashedPwdAndSalt, ","),
couch_util:to_hex(crypto:hash(sha, ClearPwd ++ Salt)) == HashedPwd;
_Else ->
false
end.
has_admins() ->
config:get("admins") /= [].
hash_admin_passwords() ->
hash_admin_passwords(true).
hash_admin_passwords(Persist) ->
lists:foreach(
fun({User, ClearPassword}) ->
HashedPassword = couch_passwords:hash_admin_password(ClearPassword),
config:set("admins", User, ?b2l(HashedPassword), Persist)
end,
couch_passwords:get_unhashed_admins()
).
init([]) ->
% Mark being able to receive documents with an _access property as a supported feature
config:enable_feature('access-ready'),
% Mark if fips is enabled
case
erlang:function_exported(crypto, info_fips, 0) andalso
crypto:info_fips() == enabled
of
true ->
config:enable_feature('fips');
false ->
ok
end,
ok = config:listen_for_changes(?MODULE, nil),
hash_admin_passwords(),
{ok, nil}.
handle_call(Msg, _From, Srv) ->
{stop, {bad_call, Msg}, Srv}.
handle_cast(Msg, Srv) ->
{stop, {bad_cast, Msg}, Srv}.
handle_info(Msg, Srv) ->
{stop, {unknown_message, Msg}, Srv}.
code_change(_OldVsn, Srv, _Extra) ->
{ok, Srv}.
terminate(_Reason, _Srv) ->
ok.
handle_config_change("admins", _, _, Persist, _) ->
% spawn here so couch event manager doesn't deadlock
{ok, spawn(fun() -> hash_admin_passwords(Persist) end)};
handle_config_change(_, _, _, _, _) ->
{ok, nil}.
handle_config_terminate(_, stop, _) ->
ok;
handle_config_terminate(_Server, _Reason, _State) ->
erlang:send_after(?RELISTEN_DELAY, whereis(?MODULE), restart_config_listener).