Add encodeURIComponent for username in API URL paths

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-07 16:58:30 +08:00
parent 984eaa96b7
commit fe4738b04c
2 changed files with 27 additions and 7 deletions

View File

@@ -162,7 +162,7 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
* @returns the result of whether the deletion is success or not. * @returns the result of whether the deletion is success or not.
*/ */
async deleteAccount(userToDelete: string): Promise<boolean> { async deleteAccount(userToDelete: string): Promise<boolean> {
const apiDelete = `/api/users/${userToDelete}`; const apiDelete = `/api/users/${encodeURIComponent(userToDelete)}`;
try { try {
const response = await apiClient.delete(apiDelete); const response = await apiClient.delete(apiDelete);
@@ -178,7 +178,7 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
* @param {object} editDetail * @param {object} editDetail
*/ */
async editAccount(userToEdit: string, editDetail: EditDetail): Promise<boolean> { async editAccount(userToEdit: string, editDetail: EditDetail): Promise<boolean> {
const apiEdit = `/api/users/${userToEdit}`; const apiEdit = `/api/users/${encodeURIComponent(userToEdit)}`;
try { try {
const response = await apiClient.put(apiEdit, { const response = await apiClient.put(apiEdit, {
@@ -194,7 +194,7 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
} }
}, },
async editAccountName(userToEdit: string, newName: string): Promise<boolean> { async editAccountName(userToEdit: string, newName: string): Promise<boolean> {
const apiEdit = `/api/users/${userToEdit}`; const apiEdit = `/api/users/${encodeURIComponent(userToEdit)}`;
try { try {
const response = await apiClient.put(apiEdit, { const response = await apiClient.put(apiEdit, {
@@ -210,7 +210,7 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
} }
}, },
async editAccountPwd(userToEdit: string, newPwd: string): Promise<boolean> { async editAccountPwd(userToEdit: string, newPwd: string): Promise<boolean> {
const apiEdit = `/api/users/${userToEdit}`; const apiEdit = `/api/users/${encodeURIComponent(userToEdit)}`;
try { try {
const response = await apiClient.put(apiEdit, { const response = await apiClient.put(apiEdit, {
@@ -231,7 +231,7 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
* @param {string} roleCode * @param {string} roleCode
*/ */
async addRoleToUser(usernameToEdit: string, roleCode: string): Promise<boolean> { async addRoleToUser(usernameToEdit: string, roleCode: string): Promise<boolean> {
const apiAddRole = `/api/users/${usernameToEdit}/roles/${roleCode}`; const apiAddRole = `/api/users/${encodeURIComponent(usernameToEdit)}/roles/${encodeURIComponent(roleCode)}`;
try { try {
const response = await apiClient.put(apiAddRole); const response = await apiClient.put(apiAddRole);
@@ -246,7 +246,7 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
* @param {string} roleCode * @param {string} roleCode
*/ */
async deleteRoleToUser(usernameToEdit: string, roleCode: string): Promise<boolean> { async deleteRoleToUser(usernameToEdit: string, roleCode: string): Promise<boolean> {
const apiDeleteRole = `/api/users/${usernameToEdit}/roles/${roleCode}`; const apiDeleteRole = `/api/users/${encodeURIComponent(usernameToEdit)}/roles/${encodeURIComponent(roleCode)}`;
try { try {
const response = await apiClient.delete(apiDeleteRole); const response = await apiClient.delete(apiDeleteRole);
@@ -261,7 +261,7 @@ export const useAcctMgmtStore = defineStore('acctMgmtStore', {
* @param {string} uniqueUsername * @param {string} uniqueUsername
*/ */
async getUserDetail(uniqueUsername: string): Promise<boolean> { async getUserDetail(uniqueUsername: string): Promise<boolean> {
const apiUserDetail = `/api/users/${uniqueUsername}`; const apiUserDetail = `/api/users/${encodeURIComponent(uniqueUsername)}`;
try { try {
const response = await apiClient.get(apiUserDetail); const response = await apiClient.get(apiUserDetail);
this.currentViewingUser = response.data; this.currentViewingUser = response.data;

View File

@@ -114,6 +114,16 @@ describe('acctMgmtStore', () => {
expect(result).toBe(true); expect(result).toBe(true);
}); });
it('encodes special characters in username', async () => {
mockDelete.mockResolvedValue({ status: 200 });
await store.deleteAccount('user@domain/name');
expect(mockDelete).toHaveBeenCalledWith(
'/api/users/user%40domain%2Fname',
);
});
it('returns false on error', async () => { it('returns false on error', async () => {
mockDelete.mockRejectedValue(new Error('fail')); mockDelete.mockRejectedValue(new Error('fail'));
@@ -154,6 +164,16 @@ describe('acctMgmtStore', () => {
); );
expect(result).toBe(true); expect(result).toBe(true);
}); });
it('encodes special characters in username and role', async () => {
mockPut.mockResolvedValue({ status: 200 });
await store.addRoleToUser('user@org', 'role/special');
expect(mockPut).toHaveBeenCalledWith(
'/api/users/user%40org/roles/role%2Fspecial',
);
});
}); });
describe('deleteRoleToUser', () => { describe('deleteRoleToUser', () => {