014 Bearer

This commit is contained in:
2025-10-20 22:29:38 +03:00
parent a8bfd3de9e
commit ab088906b8
4 changed files with 207 additions and 166 deletions
+3 -1
View File
@@ -1,7 +1,9 @@
<hr/>
t test t test
<hr/>
<cftry> <cftry>
<cfdump var=#session#/> <cfdump var=#session#/>
<cfcatch type="any"><cfdump var=#cfcatch#/></cfcatch> <cfcatch type="any"><cfdump var=#cfcatch#/></cfcatch>
</cftry/> </cftry>
<cfset jwt = new lib.jwt(this.client_secret)/> <cfset jwt = new lib.jwt(this.client_secret)/>
+85 -50
View File
@@ -22,21 +22,19 @@
<cfset this.access_token_endpoint = "https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/token" /> <cfset this.access_token_endpoint = "https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/token" />
<cfset this.idpCertUrl = "https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/certs" /> <cfset this.idpCertUrl = "https://keycloak.nubes.ru/realms/cloud/protocol/openid-connect/certs" />
<cfset this.client_secret = createObject("java", "java.lang.System").getEnv("IDP_CLIENT_SECRET")/> <cfset this.client_secret = createObject("java", "java.lang.System").getEnv("IDP_CLIENT_SECRET")/>
<cfset this.iam_url = "https://auth-api.ngcloud.ru/api/v1/auth/user"/>
<cfset this.datasource = "rpt"/> <cfset this.datasource = "rpt"/>
<cfset this.defaultdatasource = this.datasource/> <cfset this.defaultdatasource = this.datasource/>
<cfset request.DS = "#this.datasource#"> <!--- request scope is available in pseudoconstructor
<cfset request.language=""> see also https://www.bennadel.com/blog/1437-coldfusion-scope-existence-during-various-request-types-and-events.htm --->
<cfset request.DS = "#this.datasource#"/>
<cfset request.language=""/>
<cfset getDS(this.datasource)/> <cfset getDS(this.datasource)/>
<!--- кажется, нужно инициализировать датасорцы в псевдоконструкторе - onRequest не получается ---> <!--- кажется, нужно инициализировать датасорцы в псевдоконструкторе - onRequest не получается --->
<!--- *** проверить, что есть - если есть, не создавать ---> <!--- *** проверить, что есть - если есть, не создавать --->
<!--- это работает с административно заданным датасорцем, хотя, казалось бы, не должно
(без окружения функция вернет пустую структуру). Видимо, административный датасорц обнаруживается раньше.
Проверять на пустоту не хочется, чтобы не плодить в псевдоконструкторе переменные --->
<!--- <cfdump var=#variables#/> --->
<cffunction <cffunction
name="OnApplicationStart" name="OnApplicationStart"
@@ -48,7 +46,6 @@
<!--- *** мы не проверяем срок жизни сертификата и его отзыв---> <!--- *** мы не проверяем срок жизни сертификата и его отзыв--->
<!--- Obtain idp certificate OnApplicationStart ---> <!--- Obtain idp certificate OnApplicationStart --->
<cfset application.idpCertificate = getIdpCertificate(this.idpCertUrl)/> <cfset application.idpCertificate = getIdpCertificate(this.idpCertUrl)/>
<cfreturn true /> <cfreturn true />
</cffunction> </cffunction>
@@ -77,7 +74,7 @@
<!--- global settings ---> <!--- global settings --->
<cfset request.RECORDS_PER_PAGE=500/> <cfset request.RECORDS_PER_PAGE=500/>
<cfset request.APP_VERSION="0.00.013"/> <cfset request.APP_VERSION="0.00.014"/>
<cfset request.STAND=getStand()/> <cfset request.STAND=getStand()/>
<!--- application constants ---> <!--- application constants --->
@@ -168,56 +165,82 @@
(для начала делаем без рефреша, только проверяем протухание) (для начала делаем без рефреша, только проверяем протухание)
А рефреш можно сделать на серверной стороне? А рефреш можно сделать на серверной стороне?
Кладем данные из токена в реквест и наслаждаемся ---> Кладем данные из токена в реквест и наслаждаемся --->
<!--- Вариант: если есть Authorization:bearer, идем с ним к IAM --->
<!--- <cfscript> <cfscript>
var requestData = GetHttpRequestData(); var requestData = GetHttpRequestData();
var headers = requestData.headers; var headers = requestData.headers;
writedump(headers); //writedump(headers);
if (structKeyExists(headers,"authorization")) { if (structKeyExists(headers,"authorization")) {
var authorizationHeader = headers.authorization; var authorizationHeader = headers.authorization;
if (authorizationHeader.startsWith("Bearer ")) { if (authorizationHeader.startsWith("Bearer ")) {
var bearerToken = Mid(authorizationHeader, 8); var bearerToken = Mid(authorizationHeader, 8);
writeDump(bearerToken); writeDump(bearerToken);
var token=toString(binaryDecode(bearerToken, "base64")); //var token=toString(binaryDecode(bearerToken, "base64"));
writeDump(token); //writeDump(token);
jwt = new lib.jwt(this.client_secret); //jwt = new lib.jwt(this.client_secret);
lock scope="application" type="readonly" timeout="1" { //lock scope="application" type="readonly" timeout="1" {
var token_data = (jwt.decode(token, application.idpCertificate,"RS256")); // var token_data = (jwt.decode(token, application.idpCertificate,"RS256"));
//}
var result="";
//try {
var iamService = new http(method = "GET", charset = "utf-8", url = #this.iam_url#, timeout="5");
iamService.addParam(type = "HEADER", name = "Accept", value = "application/json");
iamService.addParam(type = "HEADER", name = "Authorization", value = "#authorizationHeader#"); //passthrough
//writedump(this.iamServiceUrl);abort;
var resp = iamService.send();
//if (resp.status_code NEQ 200) throw("IDP response not OK");
//writedump(resp);abort;
var prefix = resp.getPrefix();
if (prefix.status_code NEQ 200) {
//writedump(this.iamServiceUrl);
//writedump(resp);
var iamStatusCode = (isValid("integer", prefix.status_code)) ? val(prefix.status_code) : 500;
throw(message="IAM error", detail="URL: #this.iam_url# Status Code: #prefix.status_code#");
//abort;
//throw("IDP response not OK");
} }
writeDump(token_data); result = prefix.filecontent;
abort; var idpUserData=deserializeJson(result);
request.auth.wz=idpUserData.userInfo.ClientId;//GUID!
request.auth.login = idpUserData.userInfo.login;
//request.usrUid=idpUserData.userInfo.contactId;//userId; //GUID!
//writedump(idpUserData);abort;
//} catch (e) {
/*if (fileExists("#GetDirectoryFromPath(GetCurrentTemplatePath())#/etc/local-debug")) { //true for local debug without IDP, etc/* does not go to repository
var result='{"accounts":[{"login":"","type":"telegram"}],"avatar":["d2d2b7ac-50af-432b-b7c4-f7d6561e288a"],"company":"ООО «НУБЕС»","companyId":"8ec70ac0-546d-42a7-8cff-339c8fb51a23","contactId":"983967a3-58c2-4cdd-84a5-8b427ccfac82","email":"smishchuk@nubes.ru","externalUser":false,"fio":{"fullName":"Мищук Сергей ","name":"Сергей","secondName":"","surname":"Мищук"},"groupIds":["94bf5be7-52f4-5c13-87c3-513786934685","ab12b6b8-0265-4683-a5c2-5e1a74a55216","aff008c3-7443-50f6-86e1-ecff3cd3b04d","d6000da0-c9aa-55eb-9882-f118b432730b","d89a33dc-3177-5854-9580-f7f860a5ab7c","ddfe2555-9ef4-42cb-9a2b-7f24e61e7747","df12926f-ecb8-5479-857f-6d291464baad","fda5c295-230a-5025-9797-b8b4e99e08aa","2be9b4b6-94d1-59f9-a649-cab228d82169","64fe6f5c-a91b-5fea-a7ca-d9823701ebd4"],"integration":{"serviceId":""},"login":"smishchuk@nubes.ru","mobilePhone":[],"position":"","userId":"d72530e1-66a4-412f-a046-38153c4e5405"}';}
*/
//writedump(resp);
//return representationOf( {"exception"=e} );
//rethrow(e);
//}
} }
} else { } else {
writeoutput("No Authorization header present");//abort; //точно мы так не словим дедлок ВПР
}
</cfscript> ---> lock scope="session" type="exclusive" timeout="3" {
<cflock scope="session" type="exclusive" timeout="3">
<!--- //writedump(session);
<cfdump var=#session#/> //writeoutput(getTickCount()-request.startTickCount);
<cfoutput>#(getTickCount()-request.startTickCount)#</cfoutput>--->
<!--- //Это первая проба, с сессией
Это первая проба, с сессией //Когда протухнет сессия, мы можем пойти к IDP и повторно аутентифицироваться. Поскольку у нас сохраняются куки IDP, его сессия, возможно, будет жива. Если мы обновляли сессию IDP в другом приложении SSO, по всей видимости, она будет продолжаться. Если мы видим, что сессия IDP состарилась и хочет протухнуть, мы можем ее освежить и получить новый access token (который мы пока не используем - мы пользуемся ответом IDP) Наверное, правильно обходиться для этих целей вообще без сессии *** --->
Когда протухнет сессия, мы можем пойти к IDP и повторно аутентифицироваться. Поскольку у нас сохраняются куки IDP, его сессия, возможно, будет жива. Если мы обновляли сессию IDP в другом приложении SSO, по всей видимости, она будет продолжаться. Если мы видим, что сессия IDP состарилась и хочет протухнуть, мы можем ее освежить и получить новый access token (который мы пока не используем - мы пользуемся ответом IDP) Наверное, правильно обходиться для этих целей вообще без сессии *** --->
<!--- //проверить sso
проверить sso //? сделать рефреш
? сделать рефреш //? сделать логаут
? сделать логаут //? сделать отображение ошибки IDP
? сделать отображение ошибки IDP //+ сделать получение сертификата
+ сделать получение сертификата
if (structKeyExists(session, "auth")) {
request.auth = structCopy(session.auth);
} else {
//<!--- сессии нет --->
session.auth_state=createGUID().toString()
--->
<cfif structKeyExists(session, "auth")>
<cfset request.auth = structCopy(session.auth)/>
<!--- <cfset request.auth.wz = "WZ01553"/> --->
<!--- тут нужно сделать проверку и рефреш токена --->
<!--- сессия есть --->
<cfelse>
<!--- сессии нет --->
<cfset session.auth_state=createGUID().toString()/>
<cfscript>
jwt = new lib.jwt(this.client_secret); jwt = new lib.jwt(this.client_secret);
idp = new lib.oauth2(this.client_id, this.client_secret, this.auth_endpoint, this.access_token_endpoint, this.redirect_uri); idp = new lib.oauth2(this.client_id, this.client_secret, this.auth_endpoint, this.access_token_endpoint, this.redirect_uri);
@@ -229,7 +252,8 @@
Не очень понятно, как предотвратить повторный запрос (можно, конечно, использовать сессию, но странно) Не очень понятно, как предотвратить повторный запрос (можно, конечно, использовать сессию, но странно)
*/ */
//writeOutput(getTickCount()-request.startTickCount); //writeOutput(getTickCount()-request.startTickCount);
if(structKeyExists(url, "code")) { //обрабатываем редирект от IDP *** наличие поля code в URL введет нас в заблуждение, но эvar = data, label =то только без валидной сессии if(structKeyExists(url, "code")) { //обрабатываем редирект от IDP *** наличие поля code в URL введет нас в заблуждение,
//но эvar = data, label =то только без валидной сессии
//dump(var = url.code, label = "URL.code"); //dump(var = url.code, label = "URL.code");
// Request access token from idp with the // Request access token from idp with the
// authorization code that we got via the URL // authorization code that we got via the URL
@@ -255,6 +279,7 @@
session.auth.refresh_token = data.refresh_token; session.auth.refresh_token = data.refresh_token;
session.auth.id_token = data.id_token; session.auth.id_token = data.id_token;
session.auth.token_type = data.token_type; session.auth.token_type = data.token_type;
session.idp_response_content = structCopy(data); session.idp_response_content = structCopy(data);
//flush() //flush()
@@ -284,12 +309,12 @@
echo('<a href="#strURL#">self-made Auth link (idp) #strURL#</a> <br> <br>'); echo('<a href="#strURL#">self-made Auth link (idp) #strURL#</a> <br> <br>');
location(strUrl,false); location(strUrl,false);
} }
}
} // lock session exclusive
}
</cfscript> </cfscript>
</cfif>
</cflock><!--- session exclusive --->
<cfif structKeyExists(request, "auth")> <cfif structKeyExists(request, "auth")>
@@ -453,4 +478,14 @@ function getIdpCertificate(certUrl) {
} }
</cfscript> </cfscript>
<cffunction name="rethrow" returntype="void">
<!--- https://www.raymondcamden.com/2004/03/09/3089633C-9FA0-606B-3F540AE9642A795F --->
<cftry>
<cfcatch>
<cfrethrow/>
</cfcatch>
</cftry>
<cfthrow type="Context validation error" message="RETHROW() called outside TRY-CATCH"/>
</cffunction>
</cfcomponent> </cfcomponent>
+6 -5
View File
@@ -70,6 +70,7 @@ VCD Computing: <b>#dateFormat(report.qComputingAge.dt_load,'YYYY-MM-DD')# #timeF
VCD Storage: <b>#dateFormat(report.qStorageAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qStorageAge.dt_load,'HH:MM:SS')#</b> VCD Storage: <b>#dateFormat(report.qStorageAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qStorageAge.dt_load,'HH:MM:SS')#</b>
S3 хранение: <b>#dateFormat(report.qS3VolAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3VolAge.dt_load,'HH:MM:SS')#</b> S3 хранение: <b>#dateFormat(report.qS3VolAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3VolAge.dt_load,'HH:MM:SS')#</b>
S3 операции и трафик: <b>#dateFormat(report.qS3OpsTrfAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3OpsTrfAge.dt_load,'HH:MM:SS')#</b> S3 операции и трафик: <b>#dateFormat(report.qS3OpsTrfAge.dt_load,'YYYY-MM-DD')# #timeFormat(report.qS3OpsTrfAge.dt_load,'HH:MM:SS')#</b>
Цены GPL<br/>
<br/> <br/>
<a href="#request.thisPage#?output_xls" title="экспорт в Excel" style="margin-left:.5em; height:100%;" target="_blank"><img src="img/xls.gif" style="vertical-align:text-bottom;"/></a> <a href="#request.thisPage#?output_xls" title="экспорт в Excel" style="margin-left:.5em; height:100%;" target="_blank"><img src="img/xls.gif" style="vertical-align:text-bottom;"/></a>
@@ -83,7 +84,7 @@ S3 операции и трафик: <b>#dateFormat(report.qS3OpsTrfAge.dt_load,
<th width="7%">Артикул</th> <th width="7%">Артикул</th>
<th width="7%">Кол-во (метрика)</th> <th width="7%">Кол-во (метрика)</th>
<th width="3%">Ед.изм.</th> <th width="3%">Ед.изм.</th>
<th width="3%">Цена &#8381; со скид., с НДС</th> <th width="3%">Цена &#8381; GPL, с НДС</th>
<th width="3%">Стоимость &#8381; с НДС</th> <th width="3%">Стоимость &#8381; с НДС</th>
<td width="3%">&nbsp;&nbsp;&nbsp;</td> <td width="3%">&nbsp;&nbsp;&nbsp;</td>
@@ -92,8 +93,8 @@ S3 операции и трафик: <b>#dateFormat(report.qS3OpsTrfAge.dt_load,
<td width="10%">Компонент</td> <td width="10%">Компонент</td>
<!--- <td width="10%">Клиентс. назв.</td> ---> <!--- <td width="10%">Клиентс. назв.</td> --->
<td width="3%">GPL с НДС</td> <!--- <td width="3%">GPL с НДС</td>
<td width="3%">Скидка%</td> <td width="3%">Скидка%</td> --->
<!--- <td width="6%">Дата НОУ</td> <!--- <td width="6%">Дата НОУ</td>
<td width="6%">Дата оконч.</td> <td width="6%">Дата оконч.</td>
@@ -133,8 +134,8 @@ S3 операции и трафик: <b>#dateFormat(report.qS3OpsTrfAge.dt_load,
<td>#component#</td> <td>#component#</td>
<!--- <td>#user_description#</td> ---> <!--- <td>#user_description#</td> --->
<td class="r">#price#</td> <!--- <td class="r">#price#</td>
<td class="r">#discount#</td> <td class="r">#discount#</td> --->
<td class="r">#metric#</td> <td class="r">#metric#</td>
+7 -4
View File
@@ -61,13 +61,14 @@
</cffunction> </cffunction>
<!--- <cfmodule template="mod/payg.cfm" <cfmodule template="mod/payg.cfm"
dt_finish=#dt_finish# dt_finish=#dt_finish#
dt_start=#dt_start# dt_start=#dt_start#
tenant_wz_index=#tenant_wz_index#
output="report" output="report"
debug=#isDefined("DEBUG")# debug=#isDefined("DEBUG")#
/> ---> />
<!--- <cfset out={ <cfset out={
"hours"=#hours#, "hours"=#hours#,
"dtStart="=#iso8601dtFormat(dt_start)#, "dtStart="=#iso8601dtFormat(dt_start)#,
"dtFinish"=#iso8601dtFormat(dt_finish)#, "dtFinish"=#iso8601dtFormat(dt_finish)#,
@@ -77,11 +78,13 @@
"s3OpsTrfLoadedAt"="#iso8601dtFormat(report.qS3OpsTrfAge.dt_load)#", "s3OpsTrfLoadedAt"="#iso8601dtFormat(report.qS3OpsTrfAge.dt_load)#",
"records"=#report.qCharge.recordCount#, "records"=#report.qCharge.recordCount#,
"queryDurationMs"=#(getTickCount() - msStartAt)#, "queryDurationMs"=#(getTickCount() - msStartAt)#,
"wz"=#request.auth.wz#,
"login"=#request.auth.login#,
"report"=#report.qCharge.reduce((result, row) => { "report"=#report.qCharge.reduce((result, row) => {
result.append(row); result.append(row);
return result; return result;
}, [])# }, [])#
}/> ---><cfset out={"test"=true}/> }/>
<cfcontent <cfcontent
type="application/json" type="application/json"