Просмотр исходного кода

Merge pull request #1913 from emqx/cert_as_username

Support use certificate as username
turtleDeng 7 лет назад
Родитель
Сommit
71b198f543
4 измененных файлов с 73 добавлено и 41 удалено
  1. 3 3
      etc/emqx.conf
  2. 3 3
      priv/emqx.schema
  3. 37 35
      src/emqx_protocol.erl
  4. 30 0
      test/emqx_protocol_tests.erl

+ 3 - 3
etc/emqx.conf

@@ -1159,10 +1159,10 @@ listener.ssl.external.ciphers = ECDHE-ECDSA-AES256-GCM-SHA384,ECDHE-RSA-AES256-G
 ## Value: on | off
 ## listener.ssl.external.honor_cipher_order = on
 
-## Use the CN field from the client certificate as a username.
+## Use the CN, EN or CRT field from the client certificate as a username.
 ## Notice that 'verify' should be set as 'verify_peer'.
 ##
-## Value: cn | en
+## Value: cn | en | crt
 ## listener.ssl.external.peer_cert_as_username = cn
 
 ## TCP backlog for the SSL connection.
@@ -1522,7 +1522,7 @@ listener.wss.external.certfile = {{ platform_etc_dir }}/certs/cert.pem
 
 ## See: listener.ssl.$name.peer_cert_as_username
 ##
-## Value: cn | dn
+## Value: cn | dn | crt
 ## listener.wss.external.peer_cert_as_username = cn
 
 ## TCP backlog for the WebSocket/SSL connection.

+ 3 - 3
priv/emqx.schema

@@ -949,7 +949,7 @@ end}.
 ]}.
 
 {mapping, "listener.tcp.$name.peer_cert_as_username", "emqx.listeners", [
-  {datatype, {enum, [cn, dn]}}
+  {datatype, {enum, [cn, dn, crt]}}
 ]}.
 
 {mapping, "listener.tcp.$name.backlog", "emqx.listeners", [
@@ -1139,7 +1139,7 @@ end}.
 ]}.
 
 {mapping, "listener.ssl.$name.peer_cert_as_username", "emqx.listeners", [
-  {datatype, {enum, [cn, dn]}}
+  {datatype, {enum, [cn, dn, crt]}}
 ]}.
 
 %%--------------------------------------------------------------------
@@ -1400,7 +1400,7 @@ end}.
 ]}.
 
 {mapping, "listener.wss.$name.peer_cert_as_username", "emqx.listeners", [
-  {datatype, {enum, [cn, dn]}}
+  {datatype, {enum, [cn, dn, crt]}}
 ]}.
 
 {translation, "emqx.listeners", fun(Conf) ->

+ 37 - 35
src/emqx_protocol.erl

@@ -33,35 +33,34 @@
 -export([shutdown/2]).
 
 -record(pstate, {
-         zone,
-         sendfun,
-         peername,
-         peercert,
-         proto_ver,
-         proto_name,
-         ackprops,
-         client_id,
-         is_assigned,
-         conn_pid,
-         conn_props,
-         ack_props,
-         username,
-         session,
-         clean_start,
-         topic_aliases,
-         packet_size,
-         will_topic,
-         will_msg,
-         keepalive,
-         mountpoint,
-         is_super,
-         is_bridge,
-         enable_ban,
-         enable_acl,
-         recv_stats,
-         send_stats,
-         connected,
-         connected_at
+          zone,
+          sendfun,
+          peername,
+          peercert,
+          proto_ver,
+          proto_name,
+          client_id,
+          is_assigned,
+          conn_pid,
+          conn_props,
+          ack_props,
+          username,
+          session,
+          clean_start,
+          topic_aliases,
+          packet_size,
+          will_topic,
+          will_msg,
+          keepalive,
+          mountpoint,
+          is_super,
+          is_bridge,
+          enable_ban,
+          enable_acl,
+          recv_stats,
+          send_stats,
+          connected,
+          connected_at
         }).
 
 -type(state() :: #pstate{}).
@@ -71,6 +70,8 @@
 -compile(export_all).
 -endif.
 
+-define(NO_PROPS, undefined).
+
 -define(LOG(Level, Format, Args, PState),
         emqx_logger:Level([{client, PState#pstate.client_id}], "MQTT(~s@~s): " ++ Format,
                           [PState#pstate.client_id, esockd_net:format(PState#pstate.peername) | Args])).
@@ -106,9 +107,10 @@ init(#{peername := Peername, peercert := Peercert, sendfun := SendFun}, Options)
 
 init_username(Peercert, Options) ->
     case proplists:get_value(peer_cert_as_username, Options) of
-        cn -> esockd_peercert:common_name(Peercert);
-        dn -> esockd_peercert:subject(Peercert);
-        _  -> undefined
+        cn  -> esockd_peercert:common_name(Peercert);
+        dn  -> esockd_peercert:subject(Peercert);
+        crt -> Peercert;
+        _   -> undefined
     end.
 
 set_username(Username, PState = #pstate{username = undefined}) ->
@@ -603,10 +605,10 @@ send(Packet = ?PACKET(Type), PState = #pstate{proto_ver = Ver, sendfun = SendFun
 %%------------------------------------------------------------------------------
 %% Assign a clientid
 
-maybe_assign_client_id(PState = #pstate{client_id = <<>>, ackprops = AckProps}) ->
+maybe_assign_client_id(PState = #pstate{client_id = <<>>, ack_props = AckProps}) ->
     ClientId = emqx_guid:to_base62(emqx_guid:gen()),
     AckProps1 = set_property('Assigned-Client-Identifier', ClientId, AckProps),
-    PState#pstate{client_id = ClientId, is_assigned = true, ackprops = AckProps1};
+    PState#pstate{client_id = ClientId, is_assigned = true, ack_props = AckProps1};
 maybe_assign_client_id(PState) ->
     PState.
 
@@ -671,7 +673,7 @@ authenticate(Credentials, Password) ->
             {error, Error}
     end.
 
-set_property(Name, Value, undefined) ->
+set_property(Name, Value, ?NO_PROPS) ->
     #{Name => Value};
 set_property(Name, Value, Props) ->
     Props#{Name => Value}.

+ 30 - 0
test/emqx_protocol_tests.erl

@@ -0,0 +1,30 @@
+%% Copyright (c) 2018 EMQ Technologies Co., Ltd. All Rights Reserved.
+%%
+%% 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(emqx_protocol_tests).
+
+-include_lib("eunit/include/eunit.hrl").
+
+set_property_test() ->
+    ?assertEqual(#{test => test_property}, emqx_protocol:set_property(test, test_property, undefined)),
+    TestMap = #{test => test_property},
+    ?assertEqual(#{test => test_property, test1 => test_property2},
+                 emqx_protocol:set_property(test1, test_property2, TestMap)),
+    ok.
+
+init_username_test() ->
+    ?assertEqual(<<"Peercert">>,
+                 emqx_protocol:init_username(<<"Peercert">>, [{peer_cert_as_username, crt}])),
+    ?assertEqual(undefined,
+                 emqx_protocol:init_username(undefined, [{peer_cert_as_username, undefined}])).