mirror of
				https://github.com/immich-app/immich.git
				synced 2025-11-04 03:39:37 -05:00 
			
		
		
		
	fix(cli): auth file should be chmod 600 (#6925)
* wip new tests * test for auth file mode * check perms internally * chore: lint
This commit is contained in:
		
							parent
							
								
									6ed33da2a4
								
							
						
					
					
						commit
						ce6dc3b7af
					
				
							
								
								
									
										32
									
								
								cli/package-lock.json
									
									
									
										generated
									
									
									
								
							
							
						
						
									
										32
									
								
								cli/package-lock.json
									
									
									
										generated
									
									
									
								
							@ -3810,6 +3810,15 @@
 | 
				
			|||||||
        "get-func-name": "^2.0.1"
 | 
					        "get-func-name": "^2.0.1"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/lru-cache": {
 | 
				
			||||||
 | 
					      "version": "10.2.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
 | 
				
			||||||
 | 
					      "dev": true,
 | 
				
			||||||
 | 
					      "engines": {
 | 
				
			||||||
 | 
					        "node": "14 || >=16.14"
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/magic-string": {
 | 
					    "node_modules/magic-string": {
 | 
				
			||||||
      "version": "0.30.5",
 | 
					      "version": "0.30.5",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
 | 
				
			||||||
@ -4182,15 +4191,6 @@
 | 
				
			|||||||
        "url": "https://github.com/sponsors/isaacs"
 | 
					        "url": "https://github.com/sponsors/isaacs"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "node_modules/path-scurry/node_modules/lru-cache": {
 | 
					 | 
				
			||||||
      "version": "10.0.1",
 | 
					 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz",
 | 
					 | 
				
			||||||
      "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==",
 | 
					 | 
				
			||||||
      "dev": true,
 | 
					 | 
				
			||||||
      "engines": {
 | 
					 | 
				
			||||||
        "node": "14 || >=16.14"
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    "node_modules/path-type": {
 | 
					    "node_modules/path-type": {
 | 
				
			||||||
      "version": "4.0.0",
 | 
					      "version": "4.0.0",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
 | 
				
			||||||
@ -8566,6 +8566,12 @@
 | 
				
			|||||||
        "get-func-name": "^2.0.1"
 | 
					        "get-func-name": "^2.0.1"
 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "lru-cache": {
 | 
				
			||||||
 | 
					      "version": "10.2.0",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.0.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-2bIM8x+VAf6JT4bKAljS1qUWgMsqZRPGJS6FSahIMPVvctcNhyVp7AJu7quxOW9jwkryBReKZY5tY5JYv2n/7Q==",
 | 
				
			||||||
 | 
					      "dev": true
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "magic-string": {
 | 
					    "magic-string": {
 | 
				
			||||||
      "version": "0.30.5",
 | 
					      "version": "0.30.5",
 | 
				
			||||||
      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
 | 
					      "resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.30.5.tgz",
 | 
				
			||||||
@ -8834,14 +8840,6 @@
 | 
				
			|||||||
      "requires": {
 | 
					      "requires": {
 | 
				
			||||||
        "lru-cache": "^9.1.1 || ^10.0.0",
 | 
					        "lru-cache": "^9.1.1 || ^10.0.0",
 | 
				
			||||||
        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
 | 
					        "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0"
 | 
				
			||||||
      },
 | 
					 | 
				
			||||||
      "dependencies": {
 | 
					 | 
				
			||||||
        "lru-cache": {
 | 
					 | 
				
			||||||
          "version": "10.0.1",
 | 
					 | 
				
			||||||
          "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.0.1.tgz",
 | 
					 | 
				
			||||||
          "integrity": "sha512-IJ4uwUTi2qCccrioU6g9g/5rvvVl13bsdczUUcqbciD9iLr095yj8DQKdObriEvuNSx325N1rV1O0sJFszx75g==",
 | 
					 | 
				
			||||||
          "dev": true
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
      }
 | 
					      }
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
    "path-type": {
 | 
					    "path-type": {
 | 
				
			||||||
 | 
				
			|||||||
@ -8,11 +8,11 @@ export abstract class BaseCommand {
 | 
				
			|||||||
  protected user!: UserResponseDto;
 | 
					  protected user!: UserResponseDto;
 | 
				
			||||||
  protected serverVersion!: ServerVersionResponseDto;
 | 
					  protected serverVersion!: ServerVersionResponseDto;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  constructor(options: { config?: string }) {
 | 
					  constructor(options: { configDirectory?: string }) {
 | 
				
			||||||
    if (!options.config) {
 | 
					    if (!options.configDirectory) {
 | 
				
			||||||
      throw new Error('Config directory is required');
 | 
					      throw new Error('Config directory is required');
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
    this.sessionService = new SessionService(options.config);
 | 
					    this.sessionService = new SessionService(options.configDirectory);
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  public async connect(): Promise<void> {
 | 
					  public async connect(): Promise<void> {
 | 
				
			||||||
 | 
				
			|||||||
@ -82,7 +82,7 @@ export class SessionService {
 | 
				
			|||||||
      }
 | 
					      }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    await writeFile(this.authPath, yaml.stringify({ instanceUrl, apiKey }));
 | 
					    await writeFile(this.authPath, yaml.stringify({ instanceUrl, apiKey }), { mode: 0o600 });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    console.log('Wrote auth info to ' + this.authPath);
 | 
					    console.log('Wrote auth info to ' + this.authPath);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -7,7 +7,7 @@ export const TEST_AUTH_FILE = path.join(TEST_CONFIG_DIR, 'auth.yml');
 | 
				
			|||||||
export const TEST_IMMICH_INSTANCE_URL = 'https://test/api';
 | 
					export const TEST_IMMICH_INSTANCE_URL = 'https://test/api';
 | 
				
			||||||
export const TEST_IMMICH_API_KEY = 'pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg';
 | 
					export const TEST_IMMICH_API_KEY = 'pNussssKSYo5WasdgalvKJ1n9kdvaasdfbluPg';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const CLI_BASE_OPTIONS = { config: TEST_CONFIG_DIR };
 | 
					export const CLI_BASE_OPTIONS = { configDirectory: TEST_CONFIG_DIR };
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export const setup = async () => {
 | 
					export const setup = async () => {
 | 
				
			||||||
  const api = new ImmichApi(process.env.IMMICH_INSTANCE_URL as string, '');
 | 
					  const api = new ImmichApi(process.env.IMMICH_INSTANCE_URL as string, '');
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,8 @@
 | 
				
			|||||||
import { restoreTempFolder, testApp } from '@test-utils';
 | 
					import { restoreTempFolder, testApp } from '@test-utils';
 | 
				
			||||||
import { CLI_BASE_OPTIONS, setup, spyOnConsole } from 'test/cli-test-utils';
 | 
					import { CLI_BASE_OPTIONS, TEST_AUTH_FILE, deleteAuthFile, setup, spyOnConsole } from 'test/cli-test-utils';
 | 
				
			||||||
 | 
					import { readFile, stat } from 'node:fs/promises';
 | 
				
			||||||
import { LoginCommand } from '../../src/commands/login';
 | 
					import { LoginCommand } from '../../src/commands/login';
 | 
				
			||||||
 | 
					import yaml from 'yaml';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
describe(`login-key (e2e)`, () => {
 | 
					describe(`login-key (e2e)`, () => {
 | 
				
			||||||
  let apiKey: string;
 | 
					  let apiKey: string;
 | 
				
			||||||
@ -20,6 +22,7 @@ describe(`login-key (e2e)`, () => {
 | 
				
			|||||||
  afterAll(async () => {
 | 
					  afterAll(async () => {
 | 
				
			||||||
    await testApp.teardown();
 | 
					    await testApp.teardown();
 | 
				
			||||||
    await restoreTempFolder();
 | 
					    await restoreTempFolder();
 | 
				
			||||||
 | 
					    deleteAuthFile();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  beforeEach(async () => {
 | 
					  beforeEach(async () => {
 | 
				
			||||||
@ -28,6 +31,8 @@ describe(`login-key (e2e)`, () => {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    const api = await setup();
 | 
					    const api = await setup();
 | 
				
			||||||
    apiKey = api.apiKey;
 | 
					    apiKey = api.apiKey;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    deleteAuthFile();
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  it('should error when providing an invalid API key', async () => {
 | 
					  it('should error when providing an invalid API key', async () => {
 | 
				
			||||||
@ -39,4 +44,23 @@ describe(`login-key (e2e)`, () => {
 | 
				
			|||||||
  it('should log in when providing the correct API key', async () => {
 | 
					  it('should log in when providing the correct API key', async () => {
 | 
				
			||||||
    await new LoginCommand(CLI_BASE_OPTIONS).run(instanceUrl, apiKey);
 | 
					    await new LoginCommand(CLI_BASE_OPTIONS).run(instanceUrl, apiKey);
 | 
				
			||||||
  });
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should create an auth file when logging in', async () => {
 | 
				
			||||||
 | 
					    await new LoginCommand(CLI_BASE_OPTIONS).run(instanceUrl, apiKey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const data: string = await readFile(TEST_AUTH_FILE, 'utf8');
 | 
				
			||||||
 | 
					    const parsedConfig = yaml.parse(data);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expect(parsedConfig).toEqual(expect.objectContaining({ instanceUrl, apiKey }));
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  it('should create an auth file with chmod 600', async () => {
 | 
				
			||||||
 | 
					    await new LoginCommand(CLI_BASE_OPTIONS).run(instanceUrl, apiKey);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const stats = await stat(TEST_AUTH_FILE);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    const mode = (stats.mode & 0o777).toString(8);
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    expect(mode).toEqual('600');
 | 
				
			||||||
 | 
					  });
 | 
				
			||||||
});
 | 
					});
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user