|
21 | 21 | */
|
22 | 22 |
|
23 | 23 | import { suite, test } from "@testdeck/mocha";
|
24 |
| -import { expect, should, use as chaiUse } from "chai"; |
| 24 | +import { expect, should, use as chaiUse, assert } from "chai"; |
25 | 25 |
|
26 | 26 | import { Subscription } from "rxjs/Subscription";
|
27 | 27 |
|
28 | 28 | import Servient from "../src/servient";
|
29 | 29 | import ConsumedThing from "../src/consumed-thing";
|
30 |
| -import { Form, SecurityScheme } from "../src/thing-description"; |
| 30 | +import { AllOfSecurityScheme, Form, OneOfSecurityScheme, SecurityScheme } from "../src/thing-description"; |
31 | 31 | import { ProtocolClient, ProtocolClientFactory } from "../src/protocol-interfaces";
|
32 | 32 | import { Content } from "../src/content";
|
33 | 33 | import { ContentSerdes } from "../src/content-serdes";
|
@@ -805,4 +805,244 @@ class WoTClientTest {
|
805 | 805 | // eslint-disable-next-line no-unused-expressions
|
806 | 806 | expect(WoTClientTest.servient.hasClientFor(tcf2.scheme)).to.be.not.true;
|
807 | 807 | }
|
| 808 | + |
| 809 | + @test "ensure combo security - allOf"() { |
| 810 | + const ct = new ConsumedThing(WoTClientTest.servient); |
| 811 | + ct.securityDefinitions = { |
| 812 | + basic_sc: { |
| 813 | + scheme: "basic", |
| 814 | + }, |
| 815 | + opcua_secure_channel_sc: { |
| 816 | + scheme: "opcua-channel-security", |
| 817 | + }, |
| 818 | + opcua_authetication_sc: { |
| 819 | + scheme: "opcua-authentication", |
| 820 | + }, |
| 821 | + combo_sc: { |
| 822 | + scheme: "combo", |
| 823 | + allOf: ["opcua_secure_channel_sc", "opcua_authetication_sc"], |
| 824 | + }, |
| 825 | + }; |
| 826 | + ct.security = ["combo_sc"]; |
| 827 | + const pc = new TestProtocolClient(); |
| 828 | + const form: Form = { |
| 829 | + href: "https://example.com/", |
| 830 | + }; |
| 831 | + ct.ensureClientSecurity(pc, form); |
| 832 | + expect(pc.securitySchemes.length).equals(1); |
| 833 | + expect(pc.securitySchemes[0].scheme).equals("combo"); |
| 834 | + |
| 835 | + const comboScheme = pc.securitySchemes[0] as AllOfSecurityScheme; |
| 836 | + expect(comboScheme.allOf).instanceOf(Array); |
| 837 | + expect(comboScheme.allOf.length).equal(2); |
| 838 | + expect(comboScheme.allOf[0].scheme).equals("opcua-channel-security"); |
| 839 | + expect(comboScheme.allOf[1].scheme).equals("opcua-authentication"); |
| 840 | + } |
| 841 | + |
| 842 | + @test "ensure combo security - oneOf"() { |
| 843 | + const ct = new ConsumedThing(WoTClientTest.servient); |
| 844 | + ct.securityDefinitions = { |
| 845 | + basic_sc: { |
| 846 | + scheme: "basic", |
| 847 | + }, |
| 848 | + opcua_secure_channel_encrypt_sc: { |
| 849 | + scheme: "opcua-channel-security", |
| 850 | + mode: "encrypt", |
| 851 | + }, |
| 852 | + opcua_secure_channel_sign_sc: { |
| 853 | + scheme: "opcua-channel-security", |
| 854 | + mode: "sign", |
| 855 | + }, |
| 856 | + opcua_authetication_sc: { |
| 857 | + scheme: "opcua-authentication", |
| 858 | + }, |
| 859 | + comob_opcua_secure_channel: { |
| 860 | + scheme: "combo", |
| 861 | + oneOf: ["opcua_secure_channel_encrypt_sc", "opcua_secure_channel_sign_sc"], |
| 862 | + }, |
| 863 | + combo_sc: { |
| 864 | + scheme: "combo", |
| 865 | + allOf: ["comob_opcua_secure_channel", "opcua_authetication_sc"], |
| 866 | + }, |
| 867 | + }; |
| 868 | + ct.security = ["combo_sc"]; |
| 869 | + const pc = new TestProtocolClient(); |
| 870 | + const form: Form = { |
| 871 | + href: "https://example.com/", |
| 872 | + }; |
| 873 | + ct.ensureClientSecurity(pc, form); |
| 874 | + expect(pc.securitySchemes.length).equals(1); |
| 875 | + expect(pc.securitySchemes[0].scheme).equals("combo"); |
| 876 | + |
| 877 | + const comboScheme = pc.securitySchemes[0] as AllOfSecurityScheme; |
| 878 | + |
| 879 | + expect(comboScheme.allOf).instanceOf(Array); |
| 880 | + expect(comboScheme.allOf.length).equal(2); |
| 881 | + expect(comboScheme.allOf[0].scheme).equals("combo"); |
| 882 | + expect(comboScheme.allOf[1].scheme).equals("opcua-authentication"); |
| 883 | + |
| 884 | + // |
| 885 | + const firstScheme = comboScheme.allOf[0] as OneOfSecurityScheme; |
| 886 | + expect(firstScheme.scheme).equal("combo"); |
| 887 | + expect(firstScheme.oneOf).instanceOf(Array); |
| 888 | + |
| 889 | + expect(firstScheme.oneOf.length).equal(2); |
| 890 | + expect(firstScheme.oneOf[0].scheme).equal("opcua-channel-security"); |
| 891 | + expect(firstScheme.oneOf[0].scheme).equal("opcua-channel-security"); |
| 892 | + } |
| 893 | + |
| 894 | + @test "ensure combo security in form - allOf"() { |
| 895 | + const ct = new ConsumedThing(WoTClientTest.servient); |
| 896 | + ct.securityDefinitions = { |
| 897 | + basic_sc: { |
| 898 | + scheme: "basic", |
| 899 | + }, |
| 900 | + opcua_secure_channel_sc: { |
| 901 | + scheme: "opcua-channel-security", |
| 902 | + }, |
| 903 | + opcua_authetication_sc: { |
| 904 | + scheme: "opcua-authentication", |
| 905 | + }, |
| 906 | + combo_sc: { |
| 907 | + scheme: "combo", |
| 908 | + allOf: ["opcua_secure_channel_sc", "opcua_authetication_sc"], |
| 909 | + }, |
| 910 | + }; |
| 911 | + ct.security = "basic"; |
| 912 | + const pc = new TestProtocolClient(); |
| 913 | + const form: Form = { |
| 914 | + href: "https://example.com/", |
| 915 | + security: ["combo_sc"], |
| 916 | + }; |
| 917 | + ct.ensureClientSecurity(pc, form); |
| 918 | + expect(pc.securitySchemes.length).equals(1); |
| 919 | + const comboScheme = pc.securitySchemes[0] as AllOfSecurityScheme; |
| 920 | + |
| 921 | + expect(comboScheme.allOf[0].scheme).equals("opcua-channel-security"); |
| 922 | + expect(comboScheme.allOf[1].scheme).equals("opcua-authentication"); |
| 923 | + } |
| 924 | + |
| 925 | + @test "ensure no infinite loop with recursive combo security"() { |
| 926 | + const ct = new ConsumedThing(WoTClientTest.servient); |
| 927 | + ct.securityDefinitions = { |
| 928 | + // a badly designed combo that goes into infinite loop |
| 929 | + combo_sc: { |
| 930 | + scheme: "combo", |
| 931 | + allOf: ["combo_sc", "combo_sc"], |
| 932 | + }, |
| 933 | + }; |
| 934 | + ct.security = "basic"; |
| 935 | + const pc = new TestProtocolClient(); |
| 936 | + const form: Form = { |
| 937 | + href: "https://example.com/", |
| 938 | + security: ["combo_sc"], |
| 939 | + }; |
| 940 | + ct.ensureClientSecurity(pc, form); |
| 941 | + expect(pc.securitySchemes.length).equals(1); |
| 942 | + } |
| 943 | + |
| 944 | + @test "complex combo security with repeated elements"() { |
| 945 | + const ct = new ConsumedThing(WoTClientTest.servient); |
| 946 | + ct.securityDefinitions = { |
| 947 | + // a badly designed combo that goes into infinite loop |
| 948 | + a: { |
| 949 | + scheme: "a", |
| 950 | + }, |
| 951 | + b: { |
| 952 | + scheme: "b", |
| 953 | + }, |
| 954 | + c: { |
| 955 | + scheme: "c", |
| 956 | + }, |
| 957 | + combo_a_and_b: { |
| 958 | + scheme: "combo", |
| 959 | + allOf: ["a", "b"], |
| 960 | + }, |
| 961 | + combo_a_and_c: { |
| 962 | + scheme: "combo", |
| 963 | + allOf: ["a", "c"], |
| 964 | + }, |
| 965 | + combo_a_or_b: { |
| 966 | + scheme: "combo", |
| 967 | + oneOf: ["a", "b"], |
| 968 | + }, |
| 969 | + combo_of_combo: { |
| 970 | + scheme: "combo", |
| 971 | + oneOf: ["combo_a_and_b", "combo_a_and_c"], |
| 972 | + }, |
| 973 | + }; |
| 974 | + ct.security = ["combo_of_combo"]; |
| 975 | + const pc = new TestProtocolClient(); |
| 976 | + const form: Form = { |
| 977 | + href: "https://example.com/", |
| 978 | + }; |
| 979 | + ct.ensureClientSecurity(pc, form); |
| 980 | + expect(pc.securitySchemes.length).equals(1); |
| 981 | + expect(pc.securitySchemes[0].scheme).equal("combo"); |
| 982 | + const comboOfCombo = pc.securitySchemes[0] as OneOfSecurityScheme; |
| 983 | + expect(comboOfCombo.oneOf).instanceOf(Array); |
| 984 | + expect(comboOfCombo.oneOf.length).equal(2); |
| 985 | + expect(comboOfCombo.oneOf[0].scheme).equal("combo"); |
| 986 | + expect(comboOfCombo.oneOf[1].scheme).equal("combo"); |
| 987 | + |
| 988 | + const first = comboOfCombo.oneOf[0] as AllOfSecurityScheme; |
| 989 | + expect(first.allOf).instanceOf(Array); |
| 990 | + expect(first.allOf[0].scheme).equal("a"); |
| 991 | + expect(first.allOf[1].scheme).equal("b"); |
| 992 | + |
| 993 | + const second = comboOfCombo.oneOf[1] as AllOfSecurityScheme; |
| 994 | + expect(second.allOf).instanceOf(Array); |
| 995 | + expect(second.allOf[0].scheme).equal("a"); |
| 996 | + expect(second.allOf[1].scheme).equal("c"); |
| 997 | + |
| 998 | + // Verfy that a has been processed once - with strict equality |
| 999 | + const a1 = first.allOf[0]; |
| 1000 | + const a2 = second.allOf[0]; |
| 1001 | + expect(a1).equals(a2); |
| 1002 | + } |
| 1003 | + |
| 1004 | + @test "invalid combo with allOf AND onOf should be detected and throw"() { |
| 1005 | + const ct = new ConsumedThing(WoTClientTest.servient); |
| 1006 | + ct.securityDefinitions = { |
| 1007 | + // a badly designed combo has allOf and oneOf |
| 1008 | + a: { |
| 1009 | + scheme: "a", |
| 1010 | + }, |
| 1011 | + b: { |
| 1012 | + scheme: "b", |
| 1013 | + }, |
| 1014 | + combo_oneOf_and_allof: { |
| 1015 | + scheme: "combo", |
| 1016 | + allOf: ["a", "b"], |
| 1017 | + oneOf: ["a", "b"], |
| 1018 | + }, |
| 1019 | + }; |
| 1020 | + ct.security = ["combo_oneOf_and_allof"]; |
| 1021 | + const pc = new TestProtocolClient(); |
| 1022 | + const form: Form = { |
| 1023 | + href: "https://example.com/", |
| 1024 | + }; |
| 1025 | + assert.throws(() => { |
| 1026 | + ct.ensureClientSecurity(pc, form); |
| 1027 | + }, /Combo SecurityScheme 'combo_oneOf_and_allof' is invalid/); |
| 1028 | + } |
| 1029 | + |
| 1030 | + @test "invalid combo with missing allOf and oneOf should be detected and throw"() { |
| 1031 | + const ct = new ConsumedThing(WoTClientTest.servient); |
| 1032 | + ct.securityDefinitions = { |
| 1033 | + // a badly designed combo has NO allOf and NO oneOf |
| 1034 | + |
| 1035 | + combo_without_oneOf_and_without_allof: { |
| 1036 | + scheme: "combo", |
| 1037 | + }, |
| 1038 | + }; |
| 1039 | + ct.security = ["combo_without_oneOf_and_without_allof"]; |
| 1040 | + const pc = new TestProtocolClient(); |
| 1041 | + const form: Form = { |
| 1042 | + href: "https://example.com/", |
| 1043 | + }; |
| 1044 | + assert.throws(() => { |
| 1045 | + ct.ensureClientSecurity(pc, form); |
| 1046 | + }, /Combo SecurityScheme 'combo_without_oneOf_and_without_allof' is invalid/); |
| 1047 | + } |
808 | 1048 | }
|
0 commit comments