Commit 2f8cd049 authored by Jukkrapong Ponharn's avatar Jukkrapong Ponharn

Refactor PolicyRequest class and enhance getPublicIP function; add HTTP method...

Refactor PolicyRequest class and enhance getPublicIP function; add HTTP method enum and improve configuration handling
parent be32d104
...@@ -9,6 +9,16 @@ export interface ResourceParam { ...@@ -9,6 +9,16 @@ export interface ResourceParam {
[key: string]: any; [key: string]: any;
} }
/**
* HTTP Method Enum
*/
export enum HttpMethod {
GET = 'GET',
POST = 'POST',
PUT = 'PUT',
DELETE = 'DELETE',
}
/** /**
* Resource Structure * Resource Structure
*/ */
...@@ -37,10 +47,8 @@ export interface Environment { ...@@ -37,10 +47,8 @@ export interface Environment {
* Policy Request Configuration * Policy Request Configuration
*/ */
export interface PolicyConfig { export interface PolicyConfig {
subject?: Subject;
resource?: Resource; resource?: Resource;
action?: string; action?: string;
environment?: Environment;
} }
/** /**
...@@ -53,36 +61,112 @@ export interface PolicyRequestData { ...@@ -53,36 +61,112 @@ export interface PolicyRequestData {
environment: Environment; environment: Environment;
} }
async function getPublicIP(): Promise<string> { /**
* Get public IP address
* @returns Public IP address or null if failed
*/
async function getPublicIP(): Promise<string | null> {
try { try {
const response = await axios.get("https://api.ipify.org/?format=json"); const response = await axios.get('https://api.ipify.org/?format=json', {
timeout: 5000, // 5 second timeout
});
const ip = response.data?.ip; const ip = response.data?.ip;
if (!ip) { if (!ip || typeof ip !== 'string') {
throw new Error("IP address not found in response"); throw new Error('IP address not found in response');
} }
return ip; return ip;
} catch (error) { } catch (error) {
// Do not throw here to avoid breaking callers that expect a string. console.error('Failed to get public IP:', error);
// Log the error and return empty string as a safe fallback. return null;
console.error('getPublicIP error:', error);
return '';
} }
} }
/** /**
* Policy Request Utility Functions * Policy Request Instance Configuration
*/
export interface PolicyRequestInstanceConfig {
subject: Subject;
clientName: string;
ipVerify?: boolean;
}
/**
* Policy Request Class
*/ */
export class PolicyRequest { export class PolicyRequest {
private subject: Subject;
private clientName: string;
private ipVerify: boolean;
/**
* Create PolicyRequest instance
* @param config - Instance configuration with subject and clientName
*/
constructor(config: PolicyRequestInstanceConfig) {
this.subject = config.subject;
this.clientName = config.clientName;
this.ipVerify = config.ipVerify ?? false;
}
/**
* Get current subject
*/
getSubject(): Subject {
return { ...this.subject };
}
/**
* Get client name
*/
getClientName(): string {
return this.clientName;
}
/**
* Get IP Verify
*/
getIpVerify(): boolean {
return this.ipVerify;
}
/**
* Set ipVerify status
* @param ipVerify - IP verification flag
*/
setIpVerify(ipVerify: boolean): void {
this.ipVerify = ipVerify;
}
/**
* Update subject data
* @param subject - New subject data (merges with existing)
*/
updateSubject(subject: Subject): void {
this.subject = { ...this.subject, ...subject };
}
/** /**
* Create policy request and encode to Base64 * Create policy request and encode to Base64
* @param config - Policy configuration object * @param config - Policy configuration object
* @returns Base64 encoded string * @returns Base64 encoded string
*/ */
static create(config: PolicyConfig = {}): string { async create(config: Omit<PolicyConfig, 'subject'> = {}): Promise<string> {
let environment: Environment = {
"resource": {
"type": "WEB",
"name": this.clientName,
"timestamp": new Date(),
}
}
if (this.ipVerify) {
environment["public_ip"] = await getPublicIP();
}
const data: PolicyRequestData = { const data: PolicyRequestData = {
subject: config.subject || {}, subject: this.subject,
resource: { resource: {
type: config.resource?.type || 'API', type: config.resource?.type || '',
name: config.resource?.name || '', name: config.resource?.name || '',
param: { param: {
path: config.resource?.param?.path || '', path: config.resource?.param?.path || '',
...@@ -91,7 +175,7 @@ export class PolicyRequest { ...@@ -91,7 +175,7 @@ export class PolicyRequest {
...config.resource, ...config.resource,
}, },
action: config.action || '', action: config.action || '',
environment: config.environment || {}, environment: environment,
}; };
const jsonString = JSON.stringify(data); const jsonString = JSON.stringify(data);
...@@ -99,32 +183,18 @@ export class PolicyRequest { ...@@ -99,32 +183,18 @@ export class PolicyRequest {
} }
/** /**
* Create policy request headers * Create policy request headers
* @param config - Policy configuration object * @param url - API url
* @param headerKey - Custom header key name (default: 'X-Policy-Request') * @param method - HTTP method for call API
* @returns Headers object * @param headerKey - Custom header key name (default: 'X-Policy-Request')
*/ * @returns Headers object
static async createHeaders( */
clientName: string, async createHeaders(
url: string, url: string,
method: string, method: HttpMethod,
subject: Subject = {},
ipVerify: Boolean = false,
headerKey: string = 'X-Policy-Request' headerKey: string = 'X-Policy-Request'
): Promise<Record<string, string>> { ): Promise<Record<string, string>> {
let environment: any = {
"resource": {
"type": "WEB",
"name": clientName,
"timestamp": new Date(),
}
}
if (ipVerify) {
environment["public_ip"] = await getPublicIP();
}
const policyConfig = { const policyConfig = {
subject: subject,
resource: { resource: {
"type": "API", "type": "API",
"name": "", "name": "",
...@@ -132,10 +202,9 @@ export class PolicyRequest { ...@@ -132,10 +202,9 @@ export class PolicyRequest {
"path": new URL(url).pathname "path": new URL(url).pathname
} }
}, },
action: method, action: method as string,
environment: environment
} }
const encodedPolicy = PolicyRequest.create(policyConfig); const encodedPolicy = await this.create(policyConfig);
return { return {
[headerKey]: encodedPolicy, [headerKey]: encodedPolicy,
}; };
...@@ -190,35 +259,6 @@ export class PolicyRequest { ...@@ -190,35 +259,6 @@ export class PolicyRequest {
return true; return true;
} }
/**
* Create policy request with validation
* @param config - Policy configuration object
* @returns Base64 encoded string
* @throws Error if validation fails
*/
static createSafe(config: PolicyConfig = {}): string {
const data: PolicyRequestData = {
subject: config.subject || {},
resource: {
type: config.resource?.type || '',
name: config.resource?.name || '',
param: {
path: config.resource?.param?.path || '',
...config.resource?.param,
},
...config.resource,
},
action: config.action || '',
environment: config.environment || {},
};
if (!PolicyRequest.validate(data)) {
throw new Error('Invalid policy request data');
}
const jsonString = JSON.stringify(data);
return base64Encode(jsonString);
}
} }
// Default export // Default export
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment