Epitech's Blih Repository Manager

blih.js 6.3KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238
  1. #!/usr/bin/env node
  2. 'use strict';
  3. const program = require('commander');
  4. const prompt = require('prompt-sync')();
  5. const utf8 = require('utf8');
  6. const crypto = require('crypto');
  7. const request = require('request');
  8. const stringify = require('json-stable-stringify');
  9. const fs = require('fs');
  10. class Blih {
  11. constructor(options) {
  12. this.options = options;
  13. this._baseUrl = options.baseurl || 'https://blih.epitech.eu/';
  14. this._user = options.user || this.get_user();
  15. this._token = options.token || this.token_calc();
  16. this._verbose = options.verbose || false;
  17. this._userAgent = options.useragent || 'blih-' + program.version;
  18. }
  19. sign_data(data) {
  20. const sign = crypto.createHmac('sha512', this._token);
  21. sign.update(this._user);
  22. if (data)
  23. sign.update(stringify(data, {space: " "}));
  24. return {
  25. user: this._user,
  26. signature: sign.digest('hex'),
  27. data: data
  28. };
  29. }
  30. request(options, cb) {
  31. cb = cb || ((res) => { console.log(res.message); });
  32. const signed_data = this.sign_data(options.data);
  33. request({
  34. url: options.path,
  35. baseUrl: this._baseUrl,
  36. method: options.method || 'GET',
  37. body: JSON.stringify(signed_data),
  38. headers: {
  39. 'Content-Type': options.contentType || 'application/json',
  40. 'User-Agent': this._userAgent
  41. }
  42. }, function (err, res, body) {
  43. body = JSON.parse(body);
  44. if (body.error) {
  45. console.error(`HTTP Error ${res.statusCode}\nError message: '${body.error}'`);
  46. process.exit(0);
  47. }
  48. cb(body);
  49. });
  50. }
  51. get_user() {
  52. return this.options.user || process.env.LOGNAME || process.env.USER ||
  53. process.env.LNAME || process.env.USERNAME;
  54. }
  55. token_calc() {
  56. return crypto.createHash('sha512').update(prompt(`${this._user}'s password: `, {echo: '*'}) || process.exit(1)).digest('hex');
  57. }
  58. }
  59. class Subcommand {
  60. constructor(options) {
  61. this.options = options;
  62. this.usage_message = '';
  63. }
  64. usage() {
  65. console.log(this.usage_message);
  66. }
  67. }
  68. class Repository extends Subcommand {
  69. constructor(options) {
  70. super(options);
  71. this.usage_message =`
  72. Usage: ${process.argv[1]} [options] repository command ...
  73. Commands :
  74. create repo\t\t\t-- Create a repository named "repo"
  75. info repo\t\t\t-- Get the repository metadata
  76. getacl repo\t\t\t-- Get the acls set for the repository
  77. list\t\t\t-- List the repositories created
  78. setacl repo user [acl]\t-- Set (or remove) an acl for "user" on "repo"
  79. \t\t\t\tACL format:
  80. \t\t\t\t\tr for read
  81. \t\t\t\t\tw for write
  82. \t\t\t\t\ta for admin`;
  83. this.usage_message = this.usage_message.substr(3).replace(/\n\t\t/g, '\n');
  84. }
  85. create(params) {
  86. if (params.length < 1)
  87. return this.usage();
  88. const blih = new Blih(this.options);
  89. blih.request({ path: '/repositories', method:'POST', data: {
  90. name: params[0],
  91. type: 'git',
  92. description: ''
  93. }});
  94. }
  95. list() {
  96. const blih = new Blih(this.options);
  97. blih.request({ path: '/repositories' }, (res) => {
  98. if (res.hasOwnProperty("repositories"))
  99. for (let repo in res.repositories)
  100. console.log(repo);
  101. });
  102. }
  103. info(params) {
  104. if (params.length < 1)
  105. return this.usage();
  106. const blih = new Blih(this.options);
  107. blih.request({ path: `/repository/${params[0]}` }, (res) => {
  108. console.log('Repository info :');
  109. console.log(`name : ${params[0]}`);
  110. if (res.hasOwnProperty("message"))
  111. for (let info in res.message)
  112. console.log(`${info} : ${res.message[info]}`);
  113. });
  114. }
  115. delete(params) {
  116. if (params.length < 1)
  117. return this.usage();
  118. const blih = new Blih(this.options);
  119. blih.request({ path: `/repository/${params[0]}`, method:'DELETE' });
  120. }
  121. setacl(params) {
  122. if (params.length < 2)
  123. return this.usage();
  124. const blih = new Blih(this.options);
  125. blih.request({ path: `/repository/${params[0]}/acls`, method:'POST', data: {
  126. user: params[1],
  127. acl: params[2] || ''
  128. }});
  129. }
  130. getacl(params) {
  131. if (params.length < 1)
  132. return this.usage();
  133. const blih = new Blih(this.options);
  134. blih.request({ path: `/repository/${params[0]}/acls` }, (res) => {
  135. for (let info in res)
  136. console.log(`${info}:${res[info]}`);
  137. });
  138. }
  139. }
  140. class SSHKey extends Subcommand {
  141. constructor(options) {
  142. super(options);
  143. this.usage_message = `
  144. Usage: ${process.argv[1]} [options] sshkey command ...
  145. Commands :
  146. upload [file]\t\t\t-- Upload a new ssh-key
  147. list\t\t\t\t-- List the ssh-keys
  148. delete [sshkey]\t\t\t-- Delete the sshkey with comment [sshkey]`;
  149. this.usage_message = this.usage_message.substr(3).replace(/\n\t\t/g, '\n');
  150. }
  151. list() {
  152. const blih = new Blih(this.options);
  153. blih.request({ path: '/sshkeys' }, (res) => {
  154. console.log();
  155. for (let info in res)
  156. console.log(`${info}:\n${res[info]}\n`);
  157. });
  158. }
  159. upload(params) {
  160. if (params.length < 1)
  161. return this.usage();
  162. const file = params[0];
  163. const blih = new Blih(this.options);
  164. fs.readFile(file, 'utf8', (err, data) => {
  165. if (err) {
  166. console.error(`Can't open file : ${file}`);
  167. process.exit(1);
  168. }
  169. blih.request({ path: '/sshkeys', method: 'POST', data: { sshkey: data.trim() } });
  170. });
  171. }
  172. delete(params) {
  173. if (params.length < 1)
  174. return this.usage();
  175. const blih = new Blih(this.options);
  176. blih.request({ path: `/sshkey/${params[0]}`, method: 'DELETE' });
  177. }
  178. }
  179. program.version('1.7')
  180. .option('-v, --verbose', 'Enable verbose mode [ actually no difference ]')
  181. .option('-u, --user <user>', 'Set user')
  182. .option('-b, --baseurl <url>', 'Set baseurl')
  183. .option('-t, --token <token>', 'Set token')
  184. .option('-U, --useragent <agent>', 'Set useragent');
  185. program.command('repository [params...]')
  186. .description('Manages repositories')
  187. .action(function (params) {
  188. var action = params.shift();
  189. const repo = new Repository(program);
  190. if (typeof repo[action] !== 'function')
  191. action = 'usage';
  192. repo[action](params);
  193. });
  194. program.command('sshkey [params...]')
  195. .description('Manages SSH keys')
  196. .action(function (params) {
  197. var action = params.shift();
  198. const sshkeys = new SSHKey(program);
  199. if (typeof sshkeys[action] !== 'function')
  200. action = 'usage';
  201. sshkeys[action](params);
  202. });
  203. program.command('whoami')
  204. .description('Ask who you are')
  205. .action(function() {
  206. const blih = new Blih(program);
  207. blih.request({ path: "/whoami" });
  208. });
  209. program.parse(process.argv);
  210. if (program.args.length === 0)
  211. program.help();