/src/support/z_auth.erl

https://code.google.com/p/zotonic/ · Erlang · 171 lines · 111 code · 24 blank · 36 comment · 0 complexity · 1f532009611ed2d49702b0545e97ed79 MD5 · raw file

  1. %% @author Marc Worrell <marc@worrell.nl>
  2. %% @copyright 2009 Marc Worrell
  3. %% Date: 2009-04-24
  4. %%
  5. %% @doc Handle authentication of zotonic users. Also shows the logon screen when authentication is required.
  6. %% Copyright 2009 Marc Worrell
  7. %%
  8. %% Licensed under the Apache License, Version 2.0 (the "License");
  9. %% you may not use this file except in compliance with the License.
  10. %% You may obtain a copy of the License at
  11. %%
  12. %% http://www.apache.org/licenses/LICENSE-2.0
  13. %%
  14. %% Unless required by applicable law or agreed to in writing, software
  15. %% distributed under the License is distributed on an "AS IS" BASIS,
  16. %% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  17. %% See the License for the specific language governing permissions and
  18. %% limitations under the License.
  19. -module(z_auth).
  20. -author("Marc Worrell <marc@worrell.nl").
  21. %% interface functions
  22. -export([
  23. is_auth/1,
  24. is_auth_recent/1,
  25. logon/2,
  26. confirm/2,
  27. logon_pw/3,
  28. logoff/1,
  29. logon_from_session/1,
  30. user_from_page/1,
  31. user_from_session/1,
  32. is_enabled/2
  33. ]).
  34. -define(AUTH_RECENT_TIMEOUT, 600).
  35. -include_lib("zotonic.hrl").
  36. %% @doc Check if the visitor has been authenticated. Assumes a completely initalized context.
  37. %% @spec is_auth(#context{}) -> bool()
  38. is_auth(#context{user_id=undefined}) ->
  39. false;
  40. is_auth(_) ->
  41. true.
  42. is_auth_recent(#context{user_id=undefined}) ->
  43. false;
  44. is_auth_recent(#context{}=Context) ->
  45. case z_context:get_session(auth_confirm_timestamp, Context) of
  46. undefined ->
  47. false;
  48. AuthConfirmTimestamp ->
  49. ?DEBUG(AuthConfirmTimestamp),
  50. CurrentTimestamp = z_utils:now(),
  51. AuthConfirmTimestamp + ?AUTH_RECENT_TIMEOUT > CurrentTimestamp
  52. end.
  53. %% @doc Logon a username/password combination, checks passwords with m_identity.
  54. %% @spec logon_pw(Username, Password, Context) -> {bool(), NewContext}
  55. logon_pw(Username, Password, Context) ->
  56. case m_identity:check_username_pw(Username, Password, Context) of
  57. {ok, Id} ->
  58. case logon(Id, Context) of
  59. {ok, Context1} ->
  60. Context1;
  61. {error, _Reason} -> {false, Context}
  62. end;
  63. {error, _Reason} -> {false, Context}
  64. end.
  65. confirm(UserId, Context) ->
  66. % check if auth_user_id == userId??
  67. case is_enabled(UserId, Context) of
  68. true ->
  69. Context1 = z_context:set_session(auth_confirm_timestamp, z_utils:now(), Context),
  70. Context2 = z_notifier:foldl(auth_confirm, Context1, Context1),
  71. z_notifier:notify(auth_confirm_done, Context2),
  72. {ok, Context2};
  73. false ->
  74. {error, user_not_enabled}
  75. end.
  76. %% @doc Logon an user whose id we know
  77. logon(UserId, Context) ->
  78. case is_enabled(UserId, Context) of
  79. true ->
  80. Context1 = z_acl:logon(UserId, Context),
  81. Context2 = z_session_manager:rename_session(Context1),
  82. z_context:set_session(auth_user_id, UserId, Context2),
  83. z_context:set_session(auth_timestamp, calendar:universal_time(), Context2),
  84. Context3 = z_notifier:foldl(auth_logon, Context2, Context2),
  85. z_notifier:notify(auth_logon_done, Context3),
  86. {ok, Context3};
  87. false ->
  88. {error, user_not_enabled}
  89. end.
  90. %% @doc Forget about the user being logged on.
  91. %% @spec logoff(Context) -> NewContext
  92. logoff(Context) ->
  93. ContextLogOff = z_notifier:foldl(auth_logoff, Context, Context),
  94. z_context:set_session(auth_user_id, none, ContextLogOff),
  95. z_notifier:notify(auth_logoff_done, ContextLogOff),
  96. z_acl:logoff(ContextLogOff).
  97. %% @doc Return the user_id from the session
  98. user_from_session(SessionPid) ->
  99. z_session:get(auth_user_id, SessionPid).
  100. %% @doc Return the user_id from a page
  101. user_from_page(PagePid) ->
  102. user_from_session(z_session_page:session_pid(PagePid)).
  103. %% @doc Called after z_context:ensure_session.
  104. %% Check if the session contains an authenticated user id.
  105. %% When found then the user_id of the context is set.
  106. %% Also checks any automatic logon methods like "remember me" cookies.
  107. %% @spec logon_from_session(#context{}) -> #context{}
  108. logon_from_session(Context) ->
  109. case z_context:get_session(auth_user_id, Context) of
  110. none ->
  111. z_memo:set_userid(undefined),
  112. Context;
  113. undefined ->
  114. % New session, check if some module wants to log on
  115. case z_notifier:first(auth_autologon, Context) of
  116. undefined ->
  117. z_memo:set_userid(undefined),
  118. z_context:set_session(auth_user_id, none, Context);
  119. {ok, UserId} ->
  120. case logon(UserId, Context) of
  121. {ok, ContextLogon} ->
  122. z_memo:set_userid(UserId),
  123. ContextLogon;
  124. {error, _Reason} ->
  125. z_memo:set_userid(undefined),
  126. Context
  127. end
  128. end;
  129. UserId ->
  130. z_memo:set_userid(UserId),
  131. z_acl:logon(UserId, Context)
  132. end.
  133. %% @doc Check if the user is enabled, an user is enabled when the rsc is published and within its publication date range.
  134. is_enabled(UserId, Context) ->
  135. case z_notifier:first({user_is_enabled, UserId}, Context) of
  136. undefined ->
  137. Acl = m_rsc:get_acl_props(UserId, Context),
  138. case Acl#acl_props.is_published of
  139. false ->
  140. false;
  141. true ->
  142. Date = calendar:local_time(),
  143. Acl#acl_props.publication_start =< Date andalso Acl#acl_props.publication_end >= Date
  144. end;
  145. Other when is_boolean(Other) ->
  146. Other
  147. end.