Compare commits
6 Commits
5f14a4f6cb
...
ea339a1024
Author | SHA1 | Date |
---|---|---|
absc | ea339a1024 | |
absc | b1c4ab8e16 | |
absc | f51847fe97 | |
absc | 9e100b0317 | |
absc | c3731bf0f3 | |
absc | 9fd60a2dfa |
|
@ -5,6 +5,6 @@
|
|||
dudeswave_backend_supervisor,
|
||||
dudeswave_backend_auth,dudeswave_backend_user]},
|
||||
{registered,[]},
|
||||
{applications,[kernel,stdlib,erts,cowboy,ranch]},
|
||||
{applications,[kernel,stdlib,erts]},
|
||||
{mod,{dudeswave_backend_app,[]}},
|
||||
{start_phases,[]}]}.
|
||||
|
|
|
@ -24,8 +24,8 @@
|
|||
-export([init/1, handle_call/3, handle_cast/2, terminate/2]).
|
||||
|
||||
% Public API exports
|
||||
-export([auth/3, logout/2, user_details/1, new_user/3,
|
||||
update_user/4, delete_user/1]).
|
||||
-export([auth/4, logout/3, user_details/2, new_user/4,
|
||||
update_user/5, delete_user/2]).
|
||||
|
||||
%
|
||||
% Startup functions
|
||||
|
@ -47,33 +47,33 @@ init([]) ->
|
|||
% Users management
|
||||
%
|
||||
|
||||
auth(cookie, User, Cookie) ->
|
||||
gen_server:call({local, ?MODULE}, {cookie, User, Cookie});
|
||||
auth(cookie, User, Host, Cookie) ->
|
||||
gen_server:call({local, ?MODULE}, {cookie, User, Host, Cookie});
|
||||
|
||||
auth(password, User, Password) ->
|
||||
gen_server:call({local, ?MODULE}, {password, User, Password}).
|
||||
auth(password, User, Host, Password) ->
|
||||
gen_server:call({local, ?MODULE}, {password, User, Host, Password}).
|
||||
|
||||
logout(User, Cookie) ->
|
||||
gen_server:call({local, ?MODULE}, {logout, User, Cookie}).
|
||||
logout(User, Host, Cookie) ->
|
||||
gen_server:call({local, ?MODULE}, {logout, User, Host, Cookie}).
|
||||
|
||||
user_details(User) ->
|
||||
gen_server:call({local, ?MODULE}, {user_details, User}).
|
||||
user_details(User, Host) ->
|
||||
gen_server:call({local, ?MODULE}, {user_details, User, Host}).
|
||||
|
||||
new_user(User, Password, Email) ->
|
||||
gen_server:call({local, ?MODULE}, {new_user, User, Password, Email}).
|
||||
new_user(User, Host, Password, Email) ->
|
||||
gen_server:call({local, ?MODULE}, {new_user, User, Host, Password, Email}).
|
||||
|
||||
update_user(User, Name, Email, Desc) ->
|
||||
gen_server:call({local, ?MODULE}, {update_user, User, Name, Email, Desc}).
|
||||
update_user(User, Host, Name, Email, Desc) ->
|
||||
gen_server:call({local, ?MODULE}, {update_user, User, Host, Name, Email, Desc}).
|
||||
|
||||
delete_user(User) ->
|
||||
gen_server:call({local, ?MODULE}, {delete_user, User}).
|
||||
delete_user(User, Host) ->
|
||||
gen_server:call({local, ?MODULE}, {delete_user, User, Host}).
|
||||
|
||||
%
|
||||
% Callbacks
|
||||
%
|
||||
|
||||
handle_call({cookie, User, Cookie}, _From, State) ->
|
||||
case dudeswave_users:authenticate(cookie, User, Cookie) of
|
||||
handle_call({cookie, User, Host, Cookie}, _From, State) ->
|
||||
case dudeswave_users:authenticate(cookie, User, Host, Cookie) of
|
||||
true ->
|
||||
{reply, true, State};
|
||||
false ->
|
||||
|
@ -82,8 +82,8 @@ handle_call({cookie, User, Cookie}, _From, State) ->
|
|||
{reply, {error, Reason}, State}
|
||||
end;
|
||||
|
||||
handle_call({password, User, Password}, _From, State) ->
|
||||
case dudeswave_users:authenticate(password, User, Password) of
|
||||
handle_call({password, User, Host, Password}, _From, State) ->
|
||||
case dudeswave_users:authenticate(password, User, Host, Password) of
|
||||
{true, Cookie, Validity} ->
|
||||
{reply, {true, Cookie, Validity}, State};
|
||||
false ->
|
||||
|
@ -92,33 +92,33 @@ handle_call({password, User, Password}, _From, State) ->
|
|||
{reply, {error, Reason}, State}
|
||||
end;
|
||||
|
||||
handle_call({logout, User, Cookie}, _From, State) ->
|
||||
case dudeswave_users:logout(User, Cookie) of
|
||||
handle_call({logout, User, Host, Cookie}, _From, State) ->
|
||||
case dudeswave_users:logout(User, Host, Cookie) of
|
||||
ok -> {reply, ok, State};
|
||||
{error, Reason} -> {reply, {error, Reason}, State}
|
||||
end;
|
||||
|
||||
handle_call({user_details, User}, _From, State) ->
|
||||
case dudeswave_users:details(User) of
|
||||
handle_call({user_details, User, Host}, _From, State) ->
|
||||
case dudeswave_users:details(User, Host) of
|
||||
{error, not_found} -> {reply, not_found, State};
|
||||
{error, Reason} -> {reply, {error, Reason}, State};
|
||||
Val -> {reply, Val, State}
|
||||
end;
|
||||
|
||||
handle_call({new_user, User, Password, Email}, _From, State) ->
|
||||
case dudeswave_users:new(User, Password, Email) of
|
||||
handle_call({new_user, User, Host, Password, Email}, _From, State) ->
|
||||
case dudeswave_users:new(User, Host, Password, Email) of
|
||||
ok -> {reply, ok, State};
|
||||
{error, Reason} -> {reply, {error, Reason}, State}
|
||||
end;
|
||||
|
||||
handle_call({update_user, User, Name, Email, Desc}, _From, State) ->
|
||||
case dudeswave_users:update(User, Name, Email, Desc) of
|
||||
handle_call({update_user, User, Host, Name, Email, Desc}, _From, State) ->
|
||||
case dudeswave_users:update(User, Host, Name, Email, Desc) of
|
||||
ok -> {reply, ok, State};
|
||||
{error, Reason} -> {reply, {error, Reason}, State}
|
||||
end;
|
||||
|
||||
handle_call({delete_user, User}, _From, State) ->
|
||||
case dudeswave_users:delete(User) of
|
||||
handle_call({delete_user, User, Host}, _From, State) ->
|
||||
case dudeswave_users:delete(User, Host) of
|
||||
ok -> {reply, ok, State};
|
||||
{error, Reason} -> {reply, {error, Reason}, State}
|
||||
end.
|
||||
|
|
|
@ -5,6 +5,6 @@
|
|||
dudeswave_users_supervisor,
|
||||
dudeswave_users_auth,dudeswave_users_user]},
|
||||
{registered,[]},
|
||||
{applications,[kernel,stdlib,erts,cowboy,ranch]},
|
||||
{applications,[kernel,stdlib,erts]},
|
||||
{mod,{dudeswave_users_app,[]}},
|
||||
{start_phases,[]}]}.
|
||||
|
|
|
@ -1,23 +0,0 @@
|
|||
%
|
||||
% Copyright (c) 2024 Andrea Biscuola <a@abiscuola.com>
|
||||
%
|
||||
% Permission to use, copy, modify, and distribute this software for any
|
||||
% purpose with or without fee is hereby granted, provided that the above
|
||||
% copyright notice and this permission notice appear in all copies.
|
||||
%
|
||||
% THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
||||
% WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
||||
% MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
||||
% ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
||||
% WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
||||
% ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
||||
% OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
||||
%
|
||||
|
||||
-define(APPBUCK, dudeswave).
|
||||
-define(USERSBUCK, dudes).
|
||||
-define(COOKIESBUCK, cookies).
|
||||
-define(RANDBYTES, 32).
|
||||
-define(DEFVALIDITY, 365).
|
||||
-define(DUDENAME, "dudename").
|
||||
-define(DUDEAUTH, "dudeauth").
|
|
@ -20,10 +20,10 @@ Dudes authentication module
|
|||
Here lives all the functions for the APIs needed to handle users authentication.
|
||||
""".
|
||||
|
||||
-include_lib("dudeswave_users/include/defines.hrl").
|
||||
-include_lib("dudeswave_backend/include/defines.hrl").
|
||||
-include_lib("storage/include/storage.hrl").
|
||||
|
||||
-export([authenticate/3, logout/2]).
|
||||
-export([authenticate/4, logout/3]).
|
||||
|
||||
-doc """
|
||||
Verify a session with an existing cookie.
|
||||
|
@ -31,9 +31,10 @@ Verify a session with an existing cookie.
|
|||
Spec:
|
||||
|
||||
```
|
||||
-spec authenticate(Type, User, Auth) -> true | false | {true, Cookie, Validity} | {error, Reason} when
|
||||
-spec authenticate(Type, User, Host, Auth) -> true | false | {true, Cookie, Validity} | {error, Reason} when
|
||||
Type :: cookie | password,
|
||||
User :: binary(),
|
||||
Host :: binary(),
|
||||
Auth :: {cookie, binary()} | {password, binary()},
|
||||
Cookie :: binary(),
|
||||
Validity :: pos_integer(),
|
||||
|
@ -46,15 +47,18 @@ after authenticating with `Password`.
|
|||
If `Cookie` is valid, the function returns `true`. If the authentication is denied
|
||||
returns `false`
|
||||
""".
|
||||
-spec authenticate(Type, User, Auth) -> true | false | {true, Cookie, Validity} | {error, Reason} when
|
||||
-spec authenticate(Type, User, Host, Auth) -> true | false | {true, Cookie, Validity} | {error, Reason} when
|
||||
Type :: cookie | password,
|
||||
User :: binary(),
|
||||
Host :: binary(),
|
||||
Auth :: {cookie, binary()} | {password, binary()},
|
||||
Cookie :: binary(),
|
||||
Validity :: pos_integer(),
|
||||
Reason :: term().
|
||||
|
||||
authenticate(cookie, User, Cookie) ->
|
||||
authenticate(cookie, User, Host, Cookie) ->
|
||||
ComplUser = <<User/binary, "@", Host/binary>>,
|
||||
|
||||
case storage:read(?COOKIESBUCK, Cookie) of
|
||||
{ok, [R]} ->
|
||||
CurTime = calendar:now_to_universal_time(erlang:timestamp()),
|
||||
|
@ -64,7 +68,7 @@ authenticate(cookie, User, Cookie) ->
|
|||
if
|
||||
CookieTime >= CurTime ->
|
||||
if
|
||||
User =:= CookieUser -> true;
|
||||
ComplUser =:= CookieUser -> true;
|
||||
true -> false
|
||||
end;
|
||||
true -> false
|
||||
|
@ -73,8 +77,10 @@ authenticate(cookie, User, Cookie) ->
|
|||
{error, _} -> {error, service_unavailable}
|
||||
end;
|
||||
|
||||
authenticate(password, User, Password) ->
|
||||
case storage:read(?USERSBUCK, User) of
|
||||
authenticate(password, User, Host, Password) ->
|
||||
ComplUser = <<User/binary, "@", Host/binary>>,
|
||||
|
||||
case storage:read(?USERSBUCK, ComplUser) of
|
||||
{ok, [R]} ->
|
||||
Validity = case application:get_env(cookie_validity) of
|
||||
{ok, Value} ->
|
||||
|
@ -83,8 +89,8 @@ authenticate(password, User, Password) ->
|
|||
erlang:system_time(seconds) + ?DEFVALIDITY * 86400
|
||||
end,
|
||||
|
||||
{hash, Hash} = proplists:lookup(hash, R#object.metadata),
|
||||
{salt, Salt} = proplists:lookup(salt, R#object.metadata),
|
||||
{hash, Hash} = proplists:lookup(hash, R#object.value),
|
||||
{salt, Salt} = proplists:lookup(salt, R#object.value),
|
||||
{approved, Appr} = proplists:lookup(approved, R#object.metadata),
|
||||
|
||||
Auth = crypto:hash(sha256, <<Password/binary, Salt/binary>>),
|
||||
|
@ -94,7 +100,7 @@ authenticate(password, User, Password) ->
|
|||
Auth =:= Hash ->
|
||||
Cookie = base64:encode(rand:bytes(64)),
|
||||
case storage:write(?COOKIESBUCK, <<Cookie/binary>>,
|
||||
Validity, [{user, User}]) of
|
||||
Validity, [{user, ComplUser}]) of
|
||||
ok -> {true, Cookie, Validity};
|
||||
{error, Reason} -> {error, Reason}
|
||||
end;
|
||||
|
@ -110,23 +116,27 @@ Close an existing session
|
|||
Spec:
|
||||
|
||||
```
|
||||
-spec logout(User, Cookie) -> ok | {error, Reason} when
|
||||
-spec logout(User, Host, Cookie) -> ok | {error, Reason} when
|
||||
User :: binary(),
|
||||
Host :: binary(),
|
||||
Cookie :: binary(),
|
||||
Reason :: term().
|
||||
```
|
||||
|
||||
Invalidate and delete `Cookie` associated with `User` from the system.
|
||||
""".
|
||||
-spec logout(User, Cookie) -> ok | {error, Reason} when
|
||||
-spec logout(User, Host, Cookie) -> ok | {error, Reason} when
|
||||
User :: binary(),
|
||||
Host :: binary(),
|
||||
Cookie :: binary(),
|
||||
Reason :: term().
|
||||
|
||||
logout(User, Cookie) ->
|
||||
logout(User, Host, Cookie) ->
|
||||
ComplUser = <<User/binary, "@", Host/binary>>,
|
||||
|
||||
case storage:read(?COOKIESBUCK, Cookie) of
|
||||
{ok, [R]} ->
|
||||
{user, User} = proplists:lookup(user, R#object.metadata),
|
||||
{user, ComplUser} = proplists:lookup(user, R#object.metadata),
|
||||
storage:delete(?COOKIESBUCK, Cookie);
|
||||
{ok, []} ->
|
||||
{error, not_found};
|
||||
|
|
|
@ -15,10 +15,10 @@
|
|||
%
|
||||
-module(dudeswave_users_user).
|
||||
|
||||
-include_lib("dudeswave_users/include/defines.hrl").
|
||||
-include_lib("dudeswave_backend/include/defines.hrl").
|
||||
-include_lib("storage/include/storage.hrl").
|
||||
|
||||
-export([details/1, new/3, update/4, delete/1]).
|
||||
-export([details/2, new/4, update/5, delete/1]).
|
||||
|
||||
-doc """
|
||||
Return user details.
|
||||
|
@ -26,22 +26,30 @@ Return user details.
|
|||
Spec:
|
||||
|
||||
```
|
||||
-spec details(User) -> Value | {error, Reason} when
|
||||
-spec details(User, Host) -> {ok, Value} | {error, Reason} when
|
||||
User :: binary(),
|
||||
Host :: binary(),
|
||||
Value :: term(),
|
||||
Reason :: term().
|
||||
```
|
||||
""".
|
||||
-spec details(User) -> Value | {error, Reason} when
|
||||
-spec details(User, Host) -> {ok, Value} | {error, Reason} when
|
||||
User :: binary(),
|
||||
Host :: binary(),
|
||||
Value :: term(),
|
||||
Reason :: term().
|
||||
|
||||
details(User) ->
|
||||
case storage:read(?USERSBUCK, User) of
|
||||
{ok, [R]} -> R#object.value;
|
||||
{error, Reason} -> {error, Reason};
|
||||
{ok, []} -> {error, not_found}
|
||||
details(User, Host) ->
|
||||
ComplUser = <<User/binary, "@", Host/binary>>,
|
||||
|
||||
case storage:read(?USERSBUCK, ComplUser) of
|
||||
{ok, [R]} ->
|
||||
{details, Details} = proplists:lookup(details, R#object.value),
|
||||
{ok, Details};
|
||||
{error, Reason} ->
|
||||
{error, Reason};
|
||||
{ok, []} ->
|
||||
{error, not_found}
|
||||
end.
|
||||
|
||||
-doc """
|
||||
|
@ -50,8 +58,9 @@ Create a new user.
|
|||
Spec:
|
||||
|
||||
```
|
||||
-spec new(User, Password, Email) -> ok | {error, Reason} when
|
||||
-spec new(User, Host, Password, Email) -> ok | {error, Reason} when
|
||||
User :: binary(),
|
||||
Host :: binary(),
|
||||
Password :: binary(),
|
||||
Email :: binary(),
|
||||
Reason :: term().
|
||||
|
@ -62,20 +71,23 @@ The `User` is created, and stored in the application's users bucket
|
|||
|
||||
The new user is saved with a metadata `approved` of `false`,
|
||||
""".
|
||||
-spec new(User, Password, Email) -> ok | {error, Reason} when
|
||||
-spec new(User, Host, Password, Email) -> ok | {error, Reason} when
|
||||
User :: binary(),
|
||||
Host :: binary(),
|
||||
Password :: binary(),
|
||||
Email :: binary(),
|
||||
Reason :: term().
|
||||
|
||||
new(User, Password, Email) ->
|
||||
new(User, Host, Password, Email) ->
|
||||
ComplUser = <<User/binary, "@", Host/binary>>,
|
||||
|
||||
Salt = rand:bytes(?RANDBYTES),
|
||||
Hash = crypto:hash(sha256, <<Password/binary, Salt/binary>>),
|
||||
|
||||
Data = #{<<"email">> => Email},
|
||||
Metadata = [{salt, Salt}, {hash, Hash}, {approved, false}],
|
||||
Data = [{details, #{<<"email">> => Email}}, {salt, Salt}, {hash, Hash}],
|
||||
Metadata = [{approved, false}],
|
||||
|
||||
storage:write(?USERSBUCK, User, Data, Metadata).
|
||||
storage:write(?USERSBUCK, ComplUser, Data, Metadata).
|
||||
|
||||
-doc """
|
||||
Update user's details
|
||||
|
@ -83,8 +95,9 @@ Update user's details
|
|||
Spec:
|
||||
|
||||
```
|
||||
-spec update(User, Name, Email, Desc) -> ok | {error, Reason} when
|
||||
-spec update(User, Host, Name, Email, Desc) -> ok | {error, Reason} when
|
||||
User :: binary(),
|
||||
Host :: binary(),
|
||||
Name :: binary(),
|
||||
Email :: binary(),
|
||||
Desc :: binary(),
|
||||
|
@ -95,24 +108,29 @@ The details apart from `User` are updated. The username itself is immutable
|
|||
and cannot be modified. All the other fields, excluding the e-mail, are the
|
||||
ones that can be seen in the public page.
|
||||
""".
|
||||
-spec update(User, Name, Email, Desc) -> ok | {error, Reason} when
|
||||
-spec update(User, Host, Name, Email, Desc) -> ok | {error, Reason} when
|
||||
User :: binary(),
|
||||
Host :: binary(),
|
||||
Name :: binary(),
|
||||
Email :: binary(),
|
||||
Desc :: binary(),
|
||||
Reason :: term().
|
||||
|
||||
update(User, Name, Email, Desc) ->
|
||||
{ok, CurData, Metadata} = case storage:read(?USERSBUCK, User) of
|
||||
update(User, Host, Name, Email, Desc) ->
|
||||
ComplUser = <<User/binary, "@", Host/binary>>,
|
||||
|
||||
{ok, Value, Metadata} = case storage:read(?USERSBUCK, ComplUser) of
|
||||
{ok, [R]} ->
|
||||
{ok, R#object.value, R#object.metadata};
|
||||
{error, Reason} -> {error, Reason}
|
||||
end,
|
||||
|
||||
Data = CurData#{<<"email">> => Email, <<"name">> => Name,
|
||||
{details, CurDets} = proplists:lookup(details, Value),
|
||||
NewDets = CurDets#{<<"email">> => Email, <<"name">> => Name,
|
||||
<<"description">> => Desc},
|
||||
NewData = lists:keyreplace(details, 1, Value, {details, NewDets}),
|
||||
|
||||
storage:write(?USERSBUCK, User, Data, Metadata).
|
||||
storage:write(?USERSBUCK, ComplUser, NewData, Metadata).
|
||||
|
||||
-doc """
|
||||
Delete an existing user from the database.
|
||||
|
|
Loading…
Reference in New Issue