I have a mnesia table users with user and password field.
Data from my table:
[{users, <<"user_name">>, <<"password">>}].
I need to get password by user name. I make:
mnesia:dirty_read({users, <<"user_name">>}).
But it returns [].
How can I get the password by user name?
You didn't specify the record syntax you are using, but it looks like
-record(users, {username, password}).
... or something similar. So, assuming that, when you created the table, did you do anything special to set the id? In this example "username" (the first entry in the users record) should be the id by default, unless you did something special.
If you continue to have issues, consider using mnesia:match_object/1 or /3. You specify, in pattern / record syntax the portion you have to match (in this case, the username), and use ='' to match anything you don't know or care about (in this case, that'd be the password portion).
I hope that helps!
You can do something like below:
YourPasswd = mnesia:ets(fun()-> mnesia:dirty_read({users,UserId}) end),
case YourPasswd of
[] -> {error, 'No User Found'};
[{users,_UserID,Passwd}] ->
{success, Passwd}
end.
Hope data is correctly written into mnesia :)
look at this function:
-define(TABLE_NAME,users).
get_password_by_username(Username)->
F = fun(U)-> mnesia:read({?TABLE_NAME,U}) end,
mnesia:activity(transaction,F,[Username],mnesia_frag).
That will give you the result. The good thing with mnesia:activity/4 is that wether the table is fragmented or not, the answers are okay.
good luck
I understand you want your passwords checked as quickly as possible, but is your project at the stage where you need to optimize this?
I've an authentication module in one of my projects and I have a similar goals as yours. As I haven't had occasion to optimize just yet, I am using mnsesia transactions and lists for user names and passwords.
Here is part of my auth module.
-module(auth).
-export([init/1, add_user/2, start_session/2]).
-record(auth_user, {username, password}).
init(Nodes) ->
mnesia:create_table(auth_user,
[{disc_copies, Nodes},
{attributes, record_info(fields, auth_user)}]).
add_user(Username, Password) ->
T = fun() ->
mnesia:write(#auth_user {
username = Username,
password = Password})
end,
mnesia:transaction(T).
start_session(Username, Password) ->
T = fun() ->
mnesia:read(auth_user, Username)
end,
{atomic, Ret} = mnesia:transaction(T),
case Ret of
[U] ->
if (U#auth_user.password == Password) ->
true;
true ->
false
end;
_Else ->
false
end.
After compiling and starting up the erlang shell.
Eshell V5.8.3 (abort with ^G)
1> mnesia:create_schema([node()]).
ok
3> mnesia:start().
ok
4> auth:init([node()]).
{atomic,ok}
5> auth:add_user("rodericktaylor", "password").
{atomic,ok}
6> true = auth:start_session("rodericktaylor", "password").
true
7>
To see if I get the same issue you are having, I switched to binary values and did dirty reads.
start_session_dirty(Username, Password) ->
case mnesia:dirty_read(auth_user, Username) of
[U] ->
if (U#auth_user.password == Password) ->
true;
true ->
false
end;
_Else ->
false
end.
The following commands in the erl shell show that It works how you expect it to.
12> auth:add_user(<<"rodericktaylor">>, <<"binarypassword">>).
{atomic,ok}
14> true = auth:start_session_dirty(<<"rodericktaylor">>, <<"binarypassword">>).
true
15>
I hope I've helped.
Related
I have a gen_server in my cavv application that I need to start first to execute a call to. I want to use a command dispatcher for this. For a short example, this it the gen_server's API:
a gen_server: cavv_user
-module(cavv_user).
-behavior(gen_server).
-define(SERVER(UserId), {via, gproc, {n, l, {?MODULE, UserId}}}).
start_link(UserId) ->
gen_server:start_link(?SERVER(UserId), ?MODULE, [UserId], []).
change_email_address(UserId, EmailAddress) ->
gen_server:call(?SERVER(AggregateId), {execute_command, #change_user_email_address{user_id=UserId, email_address=EmailAddress}}).
Before I can call cavv_user:change_email_address(). I need to start the cavv_user. I do this is as a simple_one_for_one child in a supervisor, like so:
a supervisor: cavv_user_sup
-module(cavv_user_sup).
-behaviour(supervisor).
-define(CHILD(ChildName, Type, Args), {ChildName, {ChildName, start_link, Args}, temporary, 5000, Type, [ChildName]}).
start_link() ->
supervisor:start_link({local, ?SERVER}, ?MODULE, []).
start_child(UserId) ->
supervisor:start_child(?SERVER, [UserId]).
init([]) ->
RestartStrategy = {simple_one_for_one, 1, 5},
Children = [?CHILD(cavv_user, worker, [])],
{ok, { RestartStrategy, Children} }.
The problem I am now facing is how to dispatch commands to a cavv_user. I want to make sure the proper user is started first using start_child, and then call the cavv_user:change_email_address().
I have found this anwser, to use a dispatcher: Erlang: what supervision tree should I end with writing a task scheduler?
So I created a command dispatcher and end up with a cavv_user_dispatcher and a cavv_user_dispatcher_sup that in turn contains the cavv_user_dispatcher and the earlier cavv_user_sup:
cavv_user_dispatch_sup
| |
cavv_user_dispatcher |
(gen_server) |
|
|
cavv_user_sup
| | |
cavv_user_1...cavv_user_N
The cavv_user_dispatcher
This works beautifully.
The problem I am facing now is, how do I properly write the code in cavv_user_dispatcher? I am facing a problem with code duplication. How to properly call start_child and call the appropriate API of cavv_user?
Should I use some kind of Fun like so?
-module(cavv_user_dispatcher).
dispatch_command(UserId, Fun) ->
gen_server:call(?SERVER, {dispatch_command, {UserId, Fun}}).
handle_call({dispatch_command, {UserId, Fun}}, _From, State) ->
cavv_user_sup:start_child(UserId),
Fun(), %% How to pass: cavv_user:change_email_address(..,..)?
{reply, ok, State};
Or duplicate the cavv_user's API like so?
-module(cavv_user_dispatcher).
change_user_email_address(UserId, EmailAddress) ->
gen_server:call(?SERVER, {change_user_email_address, {UserId, EmailAddress}}).
handle_call({change_user_email_address, {UserId, EmailAddress}}, _From, State) ->
cavv_user_sup:start_child(UserId),
cavv_user:change_email_address(UserId, EmailAddress),
{reply, ok, State};
Or should I re-use the command records from cavv_user into some kind of util to properly build them and pass them around? Maybe some better way to pass the function I want to call at cavv_user?
I would like to solve the problem in the best Erlang way as possible, without code duplication.
Is your dispatcher supposed to handle other commands?
If yes then then how will the next command will come, I mean will the requester know the process pid of the user or not?
if yes then you need 2 functions, one to create a user, it will return the pid to the requester for next call, and one to handle next requests by sending the command to the given pid
if no, then you need also 2 functions, one to create the a user and store the user_id along with the user process pid and one to handle next request by retrieving the process pid and then forward it the command (I suppose this is what you want to do).
if no then you don't need to handle any command and should pass directly the email address when creating the user process. Note that this is true for all cases since you need a different interface to create a user.
I would modify your code this way (not tested, it is too late :o) !)
-module(cavv_user_dispatcher).
create_user(UserId,UserMail) ->
gen_server:call(?SERVER,{new_user,UserId,UserMail}).
% Args is a list of argument, empty if
% F needs only one argument (the user Pid)
dispatch_command(UserId, Fun, Args) ->
gen_server:call(?SERVER, {dispatch_command, {UserId, Fun,Args}}).
handle_call({dispatch_command, {UserId, Fun,Args}}, _From, State) ->
Pid = get_pid(UserId,State),
Answer = case Pid of
unknown_user_id -> unknown_user_id;
_ -> apply(Fun,[Pid|Args]),
ok
end,
{reply, Answer, State};
handle_call({new_user,UserId,UserMail},_From,State) ->
% verify that the user id does not already exists
CheckId = check_id(UserId,State),
{Answer,NewState} = case CheckId of
false -> {already_exist,State};
true -> {ok,Pid} = cavv_user_sup:start_child(UserId,UserMail)
{ok,[{UserId,Pid}|State]}
% State must be initialized as an empty list in the init function.
{reply, Answer, NewState};
...
get_pid(UserId,State) ->
proplists:get_value(UserId, State, unknown_user_id).
check_id(UserId,State) ->
not proplists:is_defined(UserId, State).
and the user supervisor mus be modified this way:
start_child(UserId,UserMail) -> % change arity in the export
supervisor:start_child(?SERVER, [UserId,UserMail]).
and then the user server:
start_link(UserId,UserMail) ->
gen_server:start_link(?SERVER(UserId), ?MODULE, [UserId,UserMail],[]).
init([UserId,UserMail]) ->
{ok,[{user_id,UserId},{user_mail,UserMail}]}.
I wrote a Hello-World module and compiled it successfully. And then I tried to learn the things under the hood by using erl_scan and erl_parse.
-module(useless).
-export([hello/0]).
hello() -> io:format("hello world\n").
and I type in the erl shell
{ok, S} = file:read_file("useless.erl").
and
{ok, T, _} = erl_scan:string(binary_to_list(S)).
It works fine apparently. But when I try erl_parse:parse_form(T).
It gives {error,{2,erl_parse,["syntax error before: ","'-'"]}}
What am I doing wrong?
Edit:
the module compile is helpful.
Also this is cool.
The function erl_parse:parse_form/1 works on one form only. So you must split the result of erl_scan:string/1 into individual forms first.
you can use erl_scan:tokens to achieve this (this code wors, but I am not sure I use the function in the right way):
-module(useless).
-export([eval/1]).
eval(File) ->
{ok, B} = file:read_file(File),
Forms = scan(erl_scan:tokens([],binary_to_list(B),1),[]),
F = fun(X) -> {ok,Y} = erl_parse:parse_form(X), Y end,
[F(X) || X <- Forms].
scan({done,{ok,T,N},S},Res) ->
scan(erl_scan:tokens([],S,N),[T|Res]);
scan(_,Res) ->
lists:reverse(Res).
Robert suggestion is to use the re-entrant feature of erl_scan:tokens/3.
The docs on this function are not explicit and it took me some time to understand that the initial string needed to be closed by eof. (if not the tokens function runs an endless loop).
Here is the code I finally came up with, I hope that will help others.
eval(File)->
{ok, Data} = file:read_file(File),
String=erlang:binary_to_list(Data),
scan(String).
scan(String) when is_list(String)->
scan(String++eof,[]). %% appended eof
scan({done, Result, LeftOverChars},Acc)->
scan_done(Result,LeftOverChars,Acc);
scan({more, Continuation},Acc)->
scan(erl_scan:tokens(Continuation,[],1),Acc);
scan(String,Acc) when is_list(String)->
scan(erl_scan:tokens([],String,1),Acc).
scan_done({error,ErrorMsg,_Location},_LeftOverChars,_Acc)->
ErrorMsg;
scan_done({eof,Location},LeftOverChars,Acc)->
Acc;
scan_done({ok,Tokens,Location},LeftOverChars,Acc)->
case erl_parse:parse_form(Tokens) of
{ok,R}->scan(LeftOverChars,Acc++[R]);
{error,R}->scan(LeftOverChars,R)
end.
I'm new to Erlang and N2O but have some experience in python web development. I want to create authentication by email address (email - password) in my application instead of using AVZ. I've created Sign Up page with this code (other code is as n2o_sample). But instead of putting user to kvs I have {error, no_container}
-module(signup_page).
-compile(export_all).
-include_lib("n2o/include/wf.hrl").
-include_lib("n2o_sample/include/users.hrl").
title() -> [ <<"Sign Up Page">> ].
main() -> #dtl{file = "login", app = n2o_sample,
bindings = [{title,title()},{body,body()}]}.
body() ->
[ #span{id=display}, #br{},
#span{body="Email: "}, #textbox{id=user}, #br{},
#span{body="Password: "}, #password{id=pass}, #br{},
#span{body="Confirm Password"}, #password{id=confirm}, #br{},
#button{id=signup, body="Sign Up",
postback=signup,source=[user,pass,confirm]}].
event(signup) ->
User = wf:q(user), Password = wf:q(pass), Confirm = wf:q(confirm),
U = kvs:get(user, User, undefined),
case U of
{ok, undefined} ->
case Password of
undefined -> wf:update(display, <<"No pass">>);
Confirm -> Status = kvs:put(#user{id=User}),
% -> {error, no_container}
io:format("~w~n", [Status]);
B -> io:format("~w~n", [B]) end;
A -> io:format("~w~n", [A]),
wf:update(display, <<"Already registered!">>) end;
event(_) -> [].
You do everything right.
The question is essentially with KVS configuring. To make KVS properly working you should do several steps:
1. Put the kvs into rebar.config
{kvs, ".*", {git, "git://github.com/synrc/kvs", {tag,"1.5.0"}}},
Use frozen tag version, e.g. "1.5.0" is the latest stable.
2. Configure KVS application in sys.config
{n2o, [{port,8000},{transition_port, 8000}]},
{kvs, [{dba,store_mnesia},
{schema, [kvs_user, kvs_acl, kvs_feed, kvs_subscription ]} ]},
I put in example N2O configuring to see where is should be placed.
3. Just after first launch of "make console" you should initialize the DB in Erlang shell:
1> kvs:join().
This will instantiate MNESIA tables.
4. After that in Erlang shell please do the check of your problem:
2> rr(kvs_user).
First load all records from kvs_user module.
Then perform your check:
3> kvs:put(#user{id="maxim#synrc.com"}).
So, I was having all kinds of trouble with CRUD operations on sets of records in one transaction. It lead me to post 2 questions here, Trouble and MoreTrouble. However, I think that both those issues where created by the following: Within my transactions, I enclosed my mnesia:writes, reads, etc. in try/catch blocks that caught everything including mnesia's aborted transactions as part of its deadlock avoidance algorithm. I.e.,
insert(Key, Value) ->
F =
fun() ->
case sc_store:lookup(Key) of
{ok, _Value} -> sc_store:replace(Key, Value);
{error, not_found} -> sc_store:insert(Key,Value)
end
end,
try
case mnesia:transaction(F) of
{atomic, Result} -> Result;
{aborted, Reason} -> ...
end
catch
Error:Reason -> ...
end
end
sc:lookup/1, for example, looked like this:
lookup(Key) ->
try
case mnesia:read(key_to_value, Key) of
[#key_to_value{type = Type, scope = Scope, value = Value}] ->
{ok, {Value, Type, Scope}};
[] ->
{error, not_found}
end
catch
_Err:Reason -> {error, Reason}
end.
I think I must have been "intercepting" / catching mnesia's dead-lock avoidance algorithm instead of letting it retry as designed.
Is that possible? If so, its a (&^& of a gotch-a for a newbee like me. If not, any ideas why this code produced so many problems for me, but removing the try/catch from the mnesia:read, etc. functions cleared up all of my problems?
Yes, I'm not sure if that's properly documented anywhere, but you should not mask out the exceptions in mnesia operations. If you do that, it looks to mnesia like your transaction fun worked as intended, even though some operations actually didn't work at all.
Is there a straightforward way to convert an Erlang fun to a string? A call to io_lib:format only prints the function reference, e.g. something like "#Fun<erl_eval.20.67289768>". For example, I'd like to be able to do this:
1> Fun = fun() -> atom_to_list('hello world') end.
2> FunStr = fun_to_str(Fun).
"fun() -> atom_to_list('hello world') end."
I'm looking for how to implement fun_to_str. In javascript, some interpreters have a .toSource() function that can be called on any object, including functions, that print their string representation. Any info is appreciated, thanks.
First, get the environment variables for the fun (which includes the abstract code):
1> {env, [{_, _, _, Abs}]} = erlang:fun_info(Fun, env).
{env,[{[],
{eval,#Fun<shell.21.83096281>},
{value,#Fun<shell.5.83096281>},
[{clause,1,[],[],
[{call,1,{atom,1,atom_to_list},[{atom,1,hello_world}]}]}]}]}
Pretty print the abstract code using erl_pp:
3> Str = erl_pp:expr({'fun', 1, {clauses, Abs}}).
[[[["fun",
[[[[["()"]," ->"],
["\n ",
[["atom_to_list",[[40,["'hello world'",41]]]]]]]]]]],
[10,["end"]]]]
4> io:format([Str|"\n"]).
fun() ->
atom_to_list('hello world')
end
ok
(You have to add {'fun', 1, {clauses, ...}} around it to make it a complete Erlang expression)
You might be able to use erlang:fun_info/2 for that, atleast i get some information from the shell when doing
1> erlang:fun_info(fun() -> test,ok end, env).
{env,[[],
{value,#Fun<shell.7.37281544>},
{eval,#Fun<shell.24.85590193>},
[{clause,1,[],[],[{atom,1,test},{atom,1,ok}]}]]}
2>
You want the last list with the clause atom and then pretty print it using for instance erl_pp