93 lines
2.7 KiB
TypeScript
93 lines
2.7 KiB
TypeScript
import { AuthConfig, BaseAuth } from '@nocobase/auth';
|
|
import { AuthModel } from '@nocobase/plugin-auth';
|
|
import axios from 'axios';
|
|
|
|
export class CASAuth extends BaseAuth {
|
|
constructor(config: AuthConfig) {
|
|
const { ctx } = config;
|
|
super({
|
|
...config,
|
|
userCollection: ctx.db.getCollection('users'),
|
|
});
|
|
}
|
|
|
|
getOptions(): {
|
|
casUrl?: string;
|
|
serviceUrl?: string;
|
|
autoSignup?: boolean;
|
|
} {
|
|
const opts = this.options || {};
|
|
return {
|
|
...opts,
|
|
serviceUrl: `${opts.serviceDomain}/api/cas:service`,
|
|
};
|
|
}
|
|
|
|
getService(authenticator: string, appName: string, redirect: string) {
|
|
const { serviceUrl } = this.getOptions();
|
|
return encodeURIComponent(`${serviceUrl}?authenticator=${authenticator}&__appName=${appName}&redirect=${redirect}`);
|
|
}
|
|
|
|
async serviceValidate(ticket: string) {
|
|
const { casUrl } = this.getOptions();
|
|
const { authenticator, __appName: appName, redirect = '/admin' } = this.ctx.action.params;
|
|
const service = this.getService(authenticator, appName, redirect);
|
|
const url = `${casUrl}/serviceValidate?ticket=${ticket}&service=${service}`;
|
|
this.ctx.logger.debug(`serviceValidate url: ${url}`, {
|
|
module: 'auth',
|
|
submodule: 'cas',
|
|
method: 'serviceValidate',
|
|
});
|
|
try {
|
|
const res = await axios.get(url);
|
|
return res;
|
|
} catch (error) {
|
|
throw new Error('CSA serviceValidate error: ' + error.message);
|
|
}
|
|
}
|
|
|
|
async validate() {
|
|
const ctx = this.ctx;
|
|
const { autoSignup } = this.getOptions();
|
|
const { ticket } = ctx.action.params;
|
|
if (!ticket) {
|
|
throw new Error('Missing ticket');
|
|
}
|
|
const res = await this.serviceValidate(ticket);
|
|
ctx.logger.debug(res?.data, { module: 'auth', submodule: 'cas', method: 'validate' });
|
|
const pattern = /<(?:cas|sso):user>(.*?)<\/(?:cas|sso):user>/;
|
|
const username = res?.data.match(pattern)?.[1];
|
|
if (!username) {
|
|
throw new Error('Invalid ticket');
|
|
}
|
|
const authenticator = this.authenticator as AuthModel;
|
|
let user = await authenticator.findUser(username);
|
|
if (user) {
|
|
return user;
|
|
}
|
|
// Bind existed user
|
|
user = await this.userRepository.findOne({
|
|
filter: { username },
|
|
});
|
|
if (user) {
|
|
await this.authenticator.addUser(user, {
|
|
through: {
|
|
uuid: username,
|
|
},
|
|
});
|
|
return user;
|
|
}
|
|
// New data
|
|
if (!autoSignup) {
|
|
throw new Error('User not found');
|
|
}
|
|
if (!this.validateUsername(username as string)) {
|
|
throw new Error('Username must be 2-16 characters in length (excluding @.<>"\'/)');
|
|
}
|
|
return await authenticator.newUser(username, {
|
|
username: username,
|
|
nickname: username,
|
|
});
|
|
}
|
|
}
|