From 8c3654696e48fff775230bcaea820a9b17fd2cbc Mon Sep 17 00:00:00 2001 From: Mevan Date: Fri, 18 Oct 2024 15:47:27 +0530 Subject: [PATCH] Add temporary API Key authenticator impl --- adapter/config/default_config.go | 3 +- adapter/config/types.go | 3 +- adapter/internal/discovery/xds/marshaller.go | 3 +- .../config/enforcer/api_key_config.pb.go | 38 +++-- .../config/enforcer/api_key_config.proto | 1 + .../config/enforcer/APIKeyConfig.java | 138 ++++++++++++++++++ .../enforcer/APIKeyConfigOrBuilder.java | 12 ++ .../config/enforcer/APIKeyConfigProto.java | 14 +- .../connect/enforcer/config/ConfigHolder.java | 1 + .../enforcer/config/dto/APIKeyDTO.java | 9 ++ .../connect/enforcer/security/AuthFilter.java | 6 +- .../security/jwt/APIKeyConstants.java | 4 +- .../enforcer/security/jwt/APIKeyUtils.java | 48 +++++- .../jwt/ChoreoAPIKeyAuthenticator.java | 102 +++++++++++++ 14 files changed, 350 insertions(+), 32 deletions(-) create mode 100644 enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/ChoreoAPIKeyAuthenticator.java diff --git a/adapter/config/default_config.go b/adapter/config/default_config.go index 75e57c01b7..c72370914b 100644 --- a/adapter/config/default_config.go +++ b/adapter/config/default_config.go @@ -228,7 +228,8 @@ var defaultConfig = &Config{ DropConsoleTestHeaders: true, }, APIKeyConfig: apiKeyConfig{ - OAuthAgentURL: "https://localhost:9443", + InternalAPIKeyHeader: "Choreo-API-Key", + OAuthAgentURL: "https://localhost:9443", }, PATConfig: patConfig{ TokenExpiryTimeSkew: 1, diff --git a/adapter/config/types.go b/adapter/config/types.go index 7ebabe44b1..f6969328d0 100644 --- a/adapter/config/types.go +++ b/adapter/config/types.go @@ -500,7 +500,8 @@ type authHeader struct { } type apiKeyConfig struct { - OAuthAgentURL string `toml:"oauthAgentURL"` + InternalAPIKeyHeader string `toml:"internalAPIKeyHeader"` + OAuthAgentURL string `toml:"oauthAgentURL"` } type patConfig struct { diff --git a/adapter/internal/discovery/xds/marshaller.go b/adapter/internal/discovery/xds/marshaller.go index 73d676a81f..9cfaf65147 100644 --- a/adapter/internal/discovery/xds/marshaller.go +++ b/adapter/internal/discovery/xds/marshaller.go @@ -219,7 +219,8 @@ func MarshalConfig(config *config.Config) *enforcer.Config { DropConsoleTestHeaders: config.Enforcer.Security.AuthHeader.DropConsoleTestHeaders, }, ApiKeyConfig: &enforcer.APIKeyConfig{ - OauthAgentURL: config.Enforcer.Security.APIKeyConfig.OAuthAgentURL, + InternalAPIKeyHeader: config.Enforcer.Security.APIKeyConfig.InternalAPIKeyHeader, + OauthAgentURL: config.Enforcer.Security.APIKeyConfig.OAuthAgentURL, }, PatConfig: &enforcer.PATConfig{ TokenExpiryTimeSkew: config.Enforcer.Security.PATConfig.TokenExpiryTimeSkew, diff --git a/adapter/pkg/discovery/api/wso2/discovery/config/enforcer/api_key_config.pb.go b/adapter/pkg/discovery/api/wso2/discovery/config/enforcer/api_key_config.pb.go index c701a7b285..926f99c7a5 100644 --- a/adapter/pkg/discovery/api/wso2/discovery/config/enforcer/api_key_config.pb.go +++ b/adapter/pkg/discovery/api/wso2/discovery/config/enforcer/api_key_config.pb.go @@ -25,7 +25,8 @@ type APIKeyConfig struct { sizeCache protoimpl.SizeCache unknownFields protoimpl.UnknownFields - OauthAgentURL string `protobuf:"bytes,1,opt,name=oauthAgentURL,proto3" json:"oauthAgentURL,omitempty"` + OauthAgentURL string `protobuf:"bytes,1,opt,name=oauthAgentURL,proto3" json:"oauthAgentURL,omitempty"` + InternalAPIKeyHeader string `protobuf:"bytes,2,opt,name=internalAPIKeyHeader,proto3" json:"internalAPIKeyHeader,omitempty"` } func (x *APIKeyConfig) Reset() { @@ -67,6 +68,13 @@ func (x *APIKeyConfig) GetOauthAgentURL() string { return "" } +func (x *APIKeyConfig) GetInternalAPIKeyHeader() string { + if x != nil { + return x.InternalAPIKeyHeader + } + return "" +} + var File_wso2_discovery_config_enforcer_api_key_config_proto protoreflect.FileDescriptor var file_wso2_discovery_config_enforcer_api_key_config_proto_rawDesc = []byte{ @@ -75,20 +83,24 @@ var file_wso2_discovery_config_enforcer_api_key_config_proto_rawDesc = []byte{ 0x2f, 0x61, 0x70, 0x69, 0x5f, 0x6b, 0x65, 0x79, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x1e, 0x77, 0x73, 0x6f, 0x32, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x65, 0x6e, 0x66, - 0x6f, 0x72, 0x63, 0x65, 0x72, 0x22, 0x34, 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x43, + 0x6f, 0x72, 0x63, 0x65, 0x72, 0x22, 0x68, 0x0a, 0x0c, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x24, 0x0a, 0x0d, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x55, 0x52, 0x4c, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x0d, 0x6f, 0x61, - 0x75, 0x74, 0x68, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x55, 0x52, 0x4c, 0x42, 0x98, 0x01, 0x0a, 0x31, - 0x6f, 0x72, 0x67, 0x2e, 0x77, 0x73, 0x6f, 0x32, 0x2e, 0x63, 0x68, 0x6f, 0x72, 0x65, 0x6f, 0x2e, - 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, - 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, - 0x72, 0x42, 0x11, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x2f, 0x67, 0x6f, - 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2d, 0x70, 0x6c, 0x61, 0x6e, 0x65, 0x2f, 0x77, - 0x73, 0x6f, 0x32, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2f, 0x63, 0x6f, - 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x72, 0x3b, 0x65, 0x6e, - 0x66, 0x6f, 0x72, 0x63, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x75, 0x74, 0x68, 0x41, 0x67, 0x65, 0x6e, 0x74, 0x55, 0x52, 0x4c, 0x12, 0x32, 0x0a, 0x14, 0x69, + 0x6e, 0x74, 0x65, 0x72, 0x6e, 0x61, 0x6c, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x48, 0x65, 0x61, + 0x64, 0x65, 0x72, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x14, 0x69, 0x6e, 0x74, 0x65, 0x72, + 0x6e, 0x61, 0x6c, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x48, 0x65, 0x61, 0x64, 0x65, 0x72, 0x42, + 0x98, 0x01, 0x0a, 0x31, 0x6f, 0x72, 0x67, 0x2e, 0x77, 0x73, 0x6f, 0x32, 0x2e, 0x63, 0x68, 0x6f, + 0x72, 0x65, 0x6f, 0x2e, 0x63, 0x6f, 0x6e, 0x6e, 0x65, 0x63, 0x74, 0x2e, 0x64, 0x69, 0x73, 0x63, + 0x6f, 0x76, 0x65, 0x72, 0x79, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x65, 0x6e, 0x66, + 0x6f, 0x72, 0x63, 0x65, 0x72, 0x42, 0x11, 0x41, 0x50, 0x49, 0x4b, 0x65, 0x79, 0x43, 0x6f, 0x6e, + 0x66, 0x69, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x4e, 0x67, 0x69, 0x74, 0x68, + 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x65, 0x6e, 0x76, 0x6f, 0x79, 0x70, 0x72, 0x6f, 0x78, + 0x79, 0x2f, 0x67, 0x6f, 0x2d, 0x63, 0x6f, 0x6e, 0x74, 0x72, 0x6f, 0x6c, 0x2d, 0x70, 0x6c, 0x61, + 0x6e, 0x65, 0x2f, 0x77, 0x73, 0x6f, 0x32, 0x2f, 0x64, 0x69, 0x73, 0x63, 0x6f, 0x76, 0x65, 0x72, + 0x79, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, + 0x72, 0x3b, 0x65, 0x6e, 0x66, 0x6f, 0x72, 0x63, 0x65, 0x72, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, + 0x6f, 0x33, } var ( diff --git a/api/proto/wso2/discovery/config/enforcer/api_key_config.proto b/api/proto/wso2/discovery/config/enforcer/api_key_config.proto index 5c1eb80496..be64efdc12 100644 --- a/api/proto/wso2/discovery/config/enforcer/api_key_config.proto +++ b/api/proto/wso2/discovery/config/enforcer/api_key_config.proto @@ -9,4 +9,5 @@ option java_multiple_files = true; message APIKeyConfig { string oauthAgentURL = 1; + string internalAPIKeyHeader = 2; } diff --git a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfig.java b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfig.java index d151a47f65..0305de475f 100644 --- a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfig.java +++ b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfig.java @@ -17,6 +17,7 @@ private APIKeyConfig(com.google.protobuf.GeneratedMessageV3.Builder builder) } private APIKeyConfig() { oauthAgentURL_ = ""; + internalAPIKeyHeader_ = ""; } @java.lang.Override @@ -55,6 +56,12 @@ private APIKeyConfig( oauthAgentURL_ = s; break; } + case 18: { + java.lang.String s = input.readStringRequireUtf8(); + + internalAPIKeyHeader_ = s; + break; + } default: { if (!parseUnknownField( input, unknownFields, extensionRegistry, tag)) { @@ -125,6 +132,44 @@ public java.lang.String getOauthAgentURL() { } } + public static final int INTERNALAPIKEYHEADER_FIELD_NUMBER = 2; + private volatile java.lang.Object internalAPIKeyHeader_; + /** + * string internalAPIKeyHeader = 2; + * @return The internalAPIKeyHeader. + */ + @java.lang.Override + public java.lang.String getInternalAPIKeyHeader() { + java.lang.Object ref = internalAPIKeyHeader_; + if (ref instanceof java.lang.String) { + return (java.lang.String) ref; + } else { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + internalAPIKeyHeader_ = s; + return s; + } + } + /** + * string internalAPIKeyHeader = 2; + * @return The bytes for internalAPIKeyHeader. + */ + @java.lang.Override + public com.google.protobuf.ByteString + getInternalAPIKeyHeaderBytes() { + java.lang.Object ref = internalAPIKeyHeader_; + if (ref instanceof java.lang.String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + internalAPIKeyHeader_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + private byte memoizedIsInitialized = -1; @java.lang.Override public final boolean isInitialized() { @@ -142,6 +187,9 @@ public void writeTo(com.google.protobuf.CodedOutputStream output) if (!getOauthAgentURLBytes().isEmpty()) { com.google.protobuf.GeneratedMessageV3.writeString(output, 1, oauthAgentURL_); } + if (!getInternalAPIKeyHeaderBytes().isEmpty()) { + com.google.protobuf.GeneratedMessageV3.writeString(output, 2, internalAPIKeyHeader_); + } unknownFields.writeTo(output); } @@ -154,6 +202,9 @@ public int getSerializedSize() { if (!getOauthAgentURLBytes().isEmpty()) { size += com.google.protobuf.GeneratedMessageV3.computeStringSize(1, oauthAgentURL_); } + if (!getInternalAPIKeyHeaderBytes().isEmpty()) { + size += com.google.protobuf.GeneratedMessageV3.computeStringSize(2, internalAPIKeyHeader_); + } size += unknownFields.getSerializedSize(); memoizedSize = size; return size; @@ -171,6 +222,8 @@ public boolean equals(final java.lang.Object obj) { if (!getOauthAgentURL() .equals(other.getOauthAgentURL())) return false; + if (!getInternalAPIKeyHeader() + .equals(other.getInternalAPIKeyHeader())) return false; if (!unknownFields.equals(other.unknownFields)) return false; return true; } @@ -184,6 +237,8 @@ public int hashCode() { hash = (19 * hash) + getDescriptor().hashCode(); hash = (37 * hash) + OAUTHAGENTURL_FIELD_NUMBER; hash = (53 * hash) + getOauthAgentURL().hashCode(); + hash = (37 * hash) + INTERNALAPIKEYHEADER_FIELD_NUMBER; + hash = (53 * hash) + getInternalAPIKeyHeader().hashCode(); hash = (29 * hash) + unknownFields.hashCode(); memoizedHashCode = hash; return hash; @@ -319,6 +374,8 @@ public Builder clear() { super.clear(); oauthAgentURL_ = ""; + internalAPIKeyHeader_ = ""; + return this; } @@ -346,6 +403,7 @@ public org.wso2.choreo.connect.discovery.config.enforcer.APIKeyConfig build() { public org.wso2.choreo.connect.discovery.config.enforcer.APIKeyConfig buildPartial() { org.wso2.choreo.connect.discovery.config.enforcer.APIKeyConfig result = new org.wso2.choreo.connect.discovery.config.enforcer.APIKeyConfig(this); result.oauthAgentURL_ = oauthAgentURL_; + result.internalAPIKeyHeader_ = internalAPIKeyHeader_; onBuilt(); return result; } @@ -398,6 +456,10 @@ public Builder mergeFrom(org.wso2.choreo.connect.discovery.config.enforcer.APIKe oauthAgentURL_ = other.oauthAgentURL_; onChanged(); } + if (!other.getInternalAPIKeyHeader().isEmpty()) { + internalAPIKeyHeader_ = other.internalAPIKeyHeader_; + onChanged(); + } this.mergeUnknownFields(other.unknownFields); onChanged(); return this; @@ -502,6 +564,82 @@ public Builder setOauthAgentURLBytes( onChanged(); return this; } + + private java.lang.Object internalAPIKeyHeader_ = ""; + /** + * string internalAPIKeyHeader = 2; + * @return The internalAPIKeyHeader. + */ + public java.lang.String getInternalAPIKeyHeader() { + java.lang.Object ref = internalAPIKeyHeader_; + if (!(ref instanceof java.lang.String)) { + com.google.protobuf.ByteString bs = + (com.google.protobuf.ByteString) ref; + java.lang.String s = bs.toStringUtf8(); + internalAPIKeyHeader_ = s; + return s; + } else { + return (java.lang.String) ref; + } + } + /** + * string internalAPIKeyHeader = 2; + * @return The bytes for internalAPIKeyHeader. + */ + public com.google.protobuf.ByteString + getInternalAPIKeyHeaderBytes() { + java.lang.Object ref = internalAPIKeyHeader_; + if (ref instanceof String) { + com.google.protobuf.ByteString b = + com.google.protobuf.ByteString.copyFromUtf8( + (java.lang.String) ref); + internalAPIKeyHeader_ = b; + return b; + } else { + return (com.google.protobuf.ByteString) ref; + } + } + /** + * string internalAPIKeyHeader = 2; + * @param value The internalAPIKeyHeader to set. + * @return This builder for chaining. + */ + public Builder setInternalAPIKeyHeader( + java.lang.String value) { + if (value == null) { + throw new NullPointerException(); + } + + internalAPIKeyHeader_ = value; + onChanged(); + return this; + } + /** + * string internalAPIKeyHeader = 2; + * @return This builder for chaining. + */ + public Builder clearInternalAPIKeyHeader() { + + internalAPIKeyHeader_ = getDefaultInstance().getInternalAPIKeyHeader(); + onChanged(); + return this; + } + /** + * string internalAPIKeyHeader = 2; + * @param value The bytes for internalAPIKeyHeader to set. + * @return This builder for chaining. + */ + public Builder setInternalAPIKeyHeaderBytes( + com.google.protobuf.ByteString value) { + if (value == null) { + throw new NullPointerException(); + } + checkByteStringIsUtf8(value); + + internalAPIKeyHeader_ = value; + onChanged(); + return this; + } @java.lang.Override public final Builder setUnknownFields( final com.google.protobuf.UnknownFieldSet unknownFields) { diff --git a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigOrBuilder.java b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigOrBuilder.java index 6a44e7b39c..18f20034b8 100644 --- a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigOrBuilder.java +++ b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigOrBuilder.java @@ -18,4 +18,16 @@ public interface APIKeyConfigOrBuilder extends */ com.google.protobuf.ByteString getOauthAgentURLBytes(); + + /** + * string internalAPIKeyHeader = 2; + * @return The internalAPIKeyHeader. + */ + java.lang.String getInternalAPIKeyHeader(); + /** + * string internalAPIKeyHeader = 2; + * @return The bytes for internalAPIKeyHeader. + */ + com.google.protobuf.ByteString + getInternalAPIKeyHeaderBytes(); } diff --git a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigProto.java b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigProto.java index fce84732e5..d9638eaa47 100644 --- a/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigProto.java +++ b/enforcer-parent/enforcer/src/main/gen/org/wso2/choreo/connect/discovery/config/enforcer/APIKeyConfigProto.java @@ -30,12 +30,12 @@ public static void registerAllExtensions( java.lang.String[] descriptorData = { "\n3wso2/discovery/config/enforcer/api_key" + "_config.proto\022\036wso2.discovery.config.enf" + - "orcer\"%\n\014APIKeyConfig\022\025\n\roauthAgentURL\030\001" + - " \001(\tB\230\001\n1org.wso2.choreo.connect.discove" + - "ry.config.enforcerB\021APIKeyConfigProtoP\001Z" + - "Ngithub.com/envoyproxy/go-control-plane/" + - "wso2/discovery/config/enforcer;enforcerb" + - "\006proto3" + "orcer\"C\n\014APIKeyConfig\022\025\n\roauthAgentURL\030\001" + + " \001(\t\022\034\n\024internalAPIKeyHeader\030\002 \001(\tB\230\001\n1o" + + "rg.wso2.choreo.connect.discovery.config." + + "enforcerB\021APIKeyConfigProtoP\001ZNgithub.co" + + "m/envoyproxy/go-control-plane/wso2/disco" + + "very/config/enforcer;enforcerb\006proto3" }; descriptor = com.google.protobuf.Descriptors.FileDescriptor .internalBuildGeneratedFileFrom(descriptorData, @@ -46,7 +46,7 @@ public static void registerAllExtensions( internal_static_wso2_discovery_config_enforcer_APIKeyConfig_fieldAccessorTable = new com.google.protobuf.GeneratedMessageV3.FieldAccessorTable( internal_static_wso2_discovery_config_enforcer_APIKeyConfig_descriptor, - new java.lang.String[] { "OauthAgentURL", }); + new java.lang.String[] { "OauthAgentURL", "InternalAPIKeyHeader", }); } // @@protoc_insertion_point(outer_class_scope) diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/ConfigHolder.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/ConfigHolder.java index 1f23e3da71..148bf9a3f6 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/ConfigHolder.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/ConfigHolder.java @@ -224,6 +224,7 @@ private void populateAuthHeaderConfigurations(AuthHeader authHeader) { private void populateAPIKeyConfigs(APIKeyConfig apiKeyConfig) { APIKeyDTO apiKeyDTO = new APIKeyDTO(); + apiKeyDTO.setApiKeyInternalHeader(apiKeyConfig.getInternalAPIKeyHeader()); apiKeyDTO.setOauthAgentURL(apiKeyConfig.getOauthAgentURL()); config.setApiKeyConfig(apiKeyDTO); } diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/dto/APIKeyDTO.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/dto/APIKeyDTO.java index 759e544628..1dab51cb5f 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/dto/APIKeyDTO.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/config/dto/APIKeyDTO.java @@ -23,8 +23,17 @@ */ public class APIKeyDTO { + private String apiKeyInternalHeader; private String oauthAgentURL; + public String getApiKeyInternalHeader() { + return apiKeyInternalHeader; + } + + public void setApiKeyInternalHeader(String apiKeyInternalHeader) { + this.apiKeyInternalHeader = apiKeyInternalHeader; + } + public String getOauthAgentURL() { return oauthAgentURL; } diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/AuthFilter.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/AuthFilter.java index 0418fc7a47..b604613e21 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/AuthFilter.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/AuthFilter.java @@ -33,7 +33,7 @@ import org.wso2.choreo.connect.enforcer.constants.AdapterConstants; import org.wso2.choreo.connect.enforcer.constants.InterceptorConstants; import org.wso2.choreo.connect.enforcer.exception.APISecurityException; -import org.wso2.choreo.connect.enforcer.security.jwt.APIKeyAuthenticator; +import org.wso2.choreo.connect.enforcer.security.jwt.ChoreoAPIKeyAuthenticator; import org.wso2.choreo.connect.enforcer.security.jwt.InternalAPIKeyAuthenticator; import org.wso2.choreo.connect.enforcer.security.jwt.JWTAuthenticator; import org.wso2.choreo.connect.enforcer.security.jwt.UnsecuredAPIAuthenticator; @@ -98,8 +98,8 @@ private void initializeAuthenticators(APIConfig apiConfig) { } if (isApiKeyProtected) { - APIKeyAuthenticator apiKeyAuthenticator = new APIKeyAuthenticator(); - authenticators.add(apiKeyAuthenticator); + ChoreoAPIKeyAuthenticator choreoApiKeyAuthenticator = new ChoreoAPIKeyAuthenticator(); + authenticators.add(choreoApiKeyAuthenticator); } Authenticator authenticator = new InternalAPIKeyAuthenticator( diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyConstants.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyConstants.java index 5345377ed1..82fc8613ba 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyConstants.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyConstants.java @@ -24,8 +24,10 @@ public class APIKeyConstants { public static final String PAT_PREFIX = "chp_"; + public static final String API_KEY_PREFIX = "chk_"; public static final String API_KEY_JSON_KEY = "key"; - public static final String PAT_EXCHANGE_ENDPOINT = "/auth/pat"; + public static final String PAT_EXCHANGE_ENDPOINT = "/internal/pat"; + public static final String API_KEY_EXCHANGE_ENDPOINT = "/internal/apiKey/token"; } diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyUtils.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyUtils.java index 55e1c021ca..a861fe162a 100644 --- a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyUtils.java +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/APIKeyUtils.java @@ -95,12 +95,12 @@ public static String generateAPIKeyHash(String apiKey) { } /** - * This function exchanges a given API key to an JWT token. + * This function exchanges a given PAT hash to an JWT token. * - * @param keyHash Key Hash + * @param patHash Key Hash * @return JWT corresponding to given PAT. */ - public static Optional exchangePATToJWT(String keyHash) { + public static Optional exchangePATToJWT(String patHash) { URL url = null; try { @@ -115,7 +115,45 @@ public static Optional exchangePATToJWT(String keyHash) { // Create a request to exchange API key to JWT. HttpPost exchangeRequest = new HttpPost(url.toURI()); exchangeRequest.addHeader("Content-Type", ContentType.APPLICATION_JSON.toString()); - exchangeRequest.setEntity(new StringEntity(createPATExchangeRequest(keyHash))); + exchangeRequest.setEntity(new StringEntity(createKeyHashExchangeRequest(patHash))); + try (CloseableHttpResponse response = httpClient.execute(exchangeRequest)) { + if (response.getStatusLine().getStatusCode() == 200) { + HttpEntity entity = response.getEntity(); + try (InputStream content = entity.getContent()) { + OAuthAgentResponse resp = gson.fromJson(IOUtils.toString(content), + OAuthAgentResponse.class); + return Optional.of(resp.getAccessToken()); + } + } + } + } catch (Exception e) { + log.error("Error occurred while exchanging API key to JWT", e); + } + return Optional.empty(); + } + + /** + * Exchange a given API key hash to a JWT token. + * + * @param apiKeyHash API Key Hash + * @return JWT corresponding to given API Key. + */ + public static Optional exchangeAPIKeyToJWT(String apiKeyHash) { + + URL url = null; + try { + String apiKeyExchangeURL = String.format("%s%s", ConfigHolder.getInstance().getConfig() + .getApiKeyConfig().getOauthAgentURL(), APIKeyConstants.API_KEY_EXCHANGE_ENDPOINT); + url = new URL(apiKeyExchangeURL); + } catch (MalformedURLException e) { + log.error("Error occurred while parsing OAuth agent URL", e); + return Optional.empty(); + } + try (CloseableHttpClient httpClient = (CloseableHttpClient) FilterUtils.getHttpClient(url.getProtocol())) { + // Create a request to exchange API key to JWT. + HttpPost exchangeRequest = new HttpPost(url.toURI()); + exchangeRequest.addHeader("Content-Type", ContentType.APPLICATION_JSON.toString()); + exchangeRequest.setEntity(new StringEntity(createKeyHashExchangeRequest(apiKeyHash))); try (CloseableHttpResponse response = httpClient.execute(exchangeRequest)) { if (response.getStatusLine().getStatusCode() == 200) { HttpEntity entity = response.getEntity(); @@ -161,7 +199,7 @@ private static String generateCRC32Checksum(String data) { return Base64.getEncoder().withoutPadding().encodeToString(checksumBytes); } - private static String createPATExchangeRequest(String keyHash) { + private static String createKeyHashExchangeRequest(String keyHash) { Map patRequest = new HashMap<>(); patRequest.put("apiKeyHash", keyHash); return gson.toJson(patRequest); diff --git a/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/ChoreoAPIKeyAuthenticator.java b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/ChoreoAPIKeyAuthenticator.java new file mode 100644 index 0000000000..22a2737799 --- /dev/null +++ b/enforcer-parent/enforcer/src/main/java/org/wso2/choreo/connect/enforcer/security/jwt/ChoreoAPIKeyAuthenticator.java @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2024, WSO2 Inc. (http://www.wso2.org) All Rights Reserved. + * + * WSO2 Inc. licenses this file to you 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. + */ + +package org.wso2.choreo.connect.enforcer.security.jwt; + +import net.minidev.json.JSONObject; +import net.minidev.json.JSONValue; +import org.apache.logging.log4j.LogManager; +import org.apache.logging.log4j.Logger; +import org.wso2.choreo.connect.enforcer.commons.model.AuthenticationContext; +import org.wso2.choreo.connect.enforcer.commons.model.RequestContext; +import org.wso2.choreo.connect.enforcer.config.ConfigHolder; +import org.wso2.choreo.connect.enforcer.constants.APIConstants; +import org.wso2.choreo.connect.enforcer.exception.APISecurityException; +import org.wso2.choreo.connect.enforcer.security.jwt.validator.JWTConstants; + +import java.util.Base64; +import java.util.Map; + +/** + * API Key authenticator. + */ +public class ChoreoAPIKeyAuthenticator extends JWTAuthenticator { + + private static final Logger log = LogManager.getLogger(ChoreoAPIKeyAuthenticator.class); + + private static boolean isAPIKeyEnabled = false; + + static { + if (System.getenv("API_KEY_ENABLED") != null) { + isAPIKeyEnabled = Boolean.parseBoolean(System.getenv("API_KEY_ENABLED")); + } + } + + public ChoreoAPIKeyAuthenticator() { + super(); + log.debug("API key authenticator initialized."); + } + + @Override + public boolean canAuthenticate(RequestContext requestContext) { + + if (!isAPIKeyEnabled) { + return false; + } + String apiKeyValue = getAPIKeyFromRequest(requestContext); + return apiKeyValue != null && apiKeyValue.startsWith(APIKeyConstants.API_KEY_PREFIX); + } + + @Override + public AuthenticationContext authenticate(RequestContext requestContext) throws APISecurityException { + + String apiKeyHeaderValue = getAPIKeyFromRequest(requestContext); + // Skipping the prefix(`chk_`) and checksum. + String apiKeyData = apiKeyHeaderValue.substring(4, apiKeyHeaderValue.length() - 6); + // Base 64 decode key data. + String decodedKeyData = new String(Base64.getDecoder().decode(apiKeyData)); + // Convert data into JSON. + JSONObject jsonObject = (JSONObject) JSONValue.parse(decodedKeyData); + // Extracting the jwt token. + String jwtToken = jsonObject.getAsString(APIKeyConstants.API_KEY_JSON_KEY); + // Add the JWT as the Authorization header to authenticate the request. + requestContext.getHeaders().put(APIConstants.AUTHORIZATION_HEADER_DEFAULT, + JWTConstants.BEARER + " " + jwtToken); + return super.authenticate(requestContext); + } + + private String getAPIKeyFromRequest(RequestContext requestContext) { + Map headers = requestContext.getHeaders(); + return headers.get(ConfigHolder.getInstance().getConfig().getApiKeyConfig().getApiKeyInternalHeader()); + } + + @Override + public String getChallengeString() { + return ""; + } + + @Override + public String getName() { + return "Choreo API Key"; + } + + @Override + public int getPriority() { + return 15; + } +}