1414
1515import com .lark .oapi .core .exception .AccessTokenNotGivenException ;
1616import com .lark .oapi .core .exception .ClientTimeoutException ;
17+ import com .lark .oapi .core .exception .ClientAssertionException ;
1718import com .lark .oapi .core .exception .IllegalAccessTokenTypeException ;
1819import com .lark .oapi .core .exception .ServerTimeoutException ;
1920import com .lark .oapi .core .httpclient .IHttpTransport ;
2324import com .lark .oapi .core .request .RequestOptions ;
2425import com .lark .oapi .core .response .RawResponse ;
2526import com .lark .oapi .core .token .AccessTokenType ;
27+ import com .lark .oapi .core .enums .AppType ;
2628import com .lark .oapi .core .utils .Jsons ;
2729import com .lark .oapi .core .utils .OKHttps ;
2830import com .lark .oapi .core .utils .Strings ;
3133
3234import java .io .InterruptedIOException ;
3335import java .nio .charset .StandardCharsets ;
36+ import java .util .HashMap ;
37+ import java .util .List ;
38+ import java .util .Locale ;
39+ import java .util .Map ;
3440import java .util .Set ;
3541
3642public class Transport {
3743
3844 private static final Logger log = LoggerFactory .getLogger (Transport .class );
3945 private static final ReqTranslator REQ_TRANSLATOR = new ReqTranslator ();
46+ private static final String OMITTED = "<omitted>" ;
4047
4148 private static AccessTokenType determineTokenType (Set <AccessTokenType > accessTokenTypeSet ,
42- RequestOptions requestOptions , boolean disableTokenCache ) {
49+ RequestOptions requestOptions , boolean disableTokenCache ,
50+ Config config ) {
51+ if (config .getClientAssertionProvider () != null ) {
52+ validateTokenType (accessTokenTypeSet , requestOptions );
53+
54+ if (Strings .isNotEmpty (requestOptions .getUserAccessToken ())
55+ && accessTokenTypeSet .contains (AccessTokenType .User )) {
56+ return AccessTokenType .User ;
57+ }
58+
59+ if (accessTokenTypeSet .contains (AccessTokenType .Tenant )) {
60+ return AccessTokenType .Tenant ;
61+ }
62+
63+ if (accessTokenTypeSet .contains (AccessTokenType .App )) {
64+ throw new ClientAssertionException (
65+ Constants .ERR_CODE_CLIENT_ASSERTION_MODE_NOT_SUPPORTED ,
66+ "AppAccessToken APIs are not available in ClientAssertion mode" );
67+ }
68+
69+ if (accessTokenTypeSet .contains (AccessTokenType .None )) {
70+ return AccessTokenType .None ;
71+ }
72+
73+ throw new IllegalAccessTokenTypeException ();
74+ }
75+
4376 if (accessTokenTypeSet .contains (AccessTokenType .None )) {
4477 return AccessTokenType .None ;
4578 }
@@ -110,7 +143,21 @@ private static void validate(Config config, RequestOptions requestOptions,
110143 throw new IllegalArgumentException ("appId is blank" );
111144 }
112145
113- if (Strings .isEmpty (config .getAppSecret ())) {
146+ if (config .getClientAssertionProvider () != null
147+ && config .getAppType () == AppType .MARKETPLACE ) {
148+ throw new ClientAssertionException (
149+ Constants .ERR_CODE_CLIENT_ASSERTION_PROVIDER_NOT_CONFIGURED ,
150+ "ClientAssertion mode is not supported for marketplace apps" );
151+ }
152+
153+ boolean hasManualAccessToken =
154+ (accessTokenType == AccessTokenType .User && Strings .isNotEmpty (requestOptions .getUserAccessToken ()))
155+ || (accessTokenType == AccessTokenType .Tenant && Strings .isNotEmpty (requestOptions .getTenantAccessToken ()))
156+ || (accessTokenType == AccessTokenType .App && Strings .isNotEmpty (requestOptions .getAppAccessToken ()));
157+
158+ if (config .getClientAssertionProvider () == null
159+ && Strings .isEmpty (config .getAppSecret ())
160+ && !hasManualAccessToken ) {
114161 throw new IllegalArgumentException ("appSecret is blank" );
115162 }
116163
@@ -176,7 +223,8 @@ public static RawResponse send(Config config
176223 // 确定token类型
177224 AccessTokenType accessTokenType = determineTokenType (accessTokenTypeSet
178225 , requestOptions
179- , config .isDisableTokenCache ());
226+ , config .isDisableTokenCache ()
227+ , config );
180228
181229 // 参数校验
182230 validate (config , requestOptions , accessTokenType );
@@ -203,16 +251,60 @@ private static void logReq(RawRequest req, String httpPath, boolean isUpload) {
203251
204252 if (!isUpload ) {
205253 log .debug ("req,path:{},header:{},body:{}" , httpPath
206- , Jsons .DEFAULT .toJson (req .getHeaders ())
207- , req . getBody () == null ? "" : Jsons . DEFAULT . toJson (req .getBody ()));
254+ , Jsons .DEFAULT .toJson (safeHeaders ( req .getHeaders () ))
255+ , safeBody (req .getBody ()));
208256 } else {
209- log .debug ("req,path:{},header:{}" , httpPath , req .getHeaders ());
257+ log .debug ("req,path:{},header:{}" , httpPath , safeHeaders ( req .getHeaders () ));
210258 }
211259 } catch (Throwable e ) {
212260 log .error ("logReq error:{}" , e );
213261 }
214262 }
215263
264+ private static Map <String , List <String >> safeHeaders (Map <String , List <String >> headers ) {
265+ Map <String , List <String >> safeHeaders = new HashMap <>();
266+ if (headers == null ) {
267+ return safeHeaders ;
268+ }
269+ headers .entrySet ().stream ().forEach (entry -> {
270+ if (!isSensitiveKey (entry .getKey ())) {
271+ safeHeaders .put (entry .getKey (), entry .getValue ());
272+ }
273+ });
274+ return safeHeaders ;
275+ }
276+
277+ private static String safeBody (Object body ) {
278+ if (body == null ) {
279+ return "" ;
280+ }
281+ String json = Jsons .DEFAULT .toJson (body );
282+ return containsSensitiveField (json ) ? OMITTED : json ;
283+ }
284+
285+ private static boolean containsSensitiveField (String json ) {
286+ if (Strings .isEmpty (json )) {
287+ return false ;
288+ }
289+ String normalized = json .toLowerCase (Locale .ROOT );
290+ return normalized .contains ("\" client_secret\" " )
291+ || normalized .contains ("\" clientassertion\" " )
292+ || normalized .contains ("\" client_assertion\" " )
293+ || normalized .contains ("\" refresh_token\" " )
294+ || normalized .contains ("\" access_token\" " )
295+ || normalized .contains ("\" tenant_access_token\" " )
296+ || normalized .contains ("\" app_access_token\" " );
297+ }
298+
299+ private static boolean isSensitiveKey (String key ) {
300+ if (Strings .isEmpty (key )) {
301+ return false ;
302+ }
303+ String normalized = key .toLowerCase (Locale .ROOT );
304+ return "authorization" .equals (normalized )
305+ || Constants .X_HELPDESK_AUTHORIZATION .toLowerCase (Locale .ROOT ).equals (normalized );
306+ }
307+
216308 private static RawResponse doSend (Config config , String httpMethod , String httpPath ,
217309 AccessTokenType accessTokenType , Object req , RequestOptions requestOptions ) throws Exception {
218310 Exception error = null ;
0 commit comments