Epitech's Blih Repository Manager

blih.js 6.2KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240
  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\tCreate a repository named "repo"
  75. info repo\t\t\tGet the repository metadata
  76. getacl repo\t\tGet the acls set for the repository
  77. list\t\t\tList the repositories created
  78. setacl repo user [acl]\tSet (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. `;
  84. this.usage_message = '\n ' + this.usage_message.substr(3).replace(/\n\t\t/g, '\n ');
  85. }
  86. create(params) {
  87. if (params.length < 1)
  88. return this.usage();
  89. const blih = new Blih(this.options);
  90. blih.request({ path: '/repositories', method:'POST', data: {
  91. name: params[0],
  92. type: 'git',
  93. description: ''
  94. }});
  95. }
  96. list() {
  97. const blih = new Blih(this.options);
  98. blih.request({ path: '/repositories' }, (res) => {
  99. if (res.hasOwnProperty("repositories"))
  100. for (let repo in res.repositories)
  101. console.log(repo);
  102. });
  103. }
  104. info(params) {
  105. if (params.length < 1)
  106. return this.usage();
  107. const blih = new Blih(this.options);
  108. blih.request({ path: `/repository/${params[0]}` }, (res) => {
  109. console.log('Repository info :');
  110. console.log(`name : ${params[0]}`);
  111. if (res.hasOwnProperty("message"))
  112. for (let info in res.message)
  113. console.log(`${info} : ${res.message[info]}`);
  114. });
  115. }
  116. delete(params) {
  117. if (params.length < 1)
  118. return this.usage();
  119. const blih = new Blih(this.options);
  120. blih.request({ path: `/repository/${params[0]}`, method:'DELETE' });
  121. }
  122. setacl(params) {
  123. if (params.length < 2)
  124. return this.usage();
  125. const blih = new Blih(this.options);
  126. blih.request({ path: `/repository/${params[0]}/acls`, method:'POST', data: {
  127. user: params[1],
  128. acl: params[2] || ''
  129. }});
  130. }
  131. getacl(params) {
  132. if (params.length < 1)
  133. return this.usage();
  134. const blih = new Blih(this.options);
  135. blih.request({ path: `/repository/${params[0]}/acls` }, (res) => {
  136. for (let info in res)
  137. console.log(`${info}:${res[info]}`);
  138. });
  139. }
  140. }
  141. class SSHKey extends Subcommand {
  142. constructor(options) {
  143. super(options);
  144. this.usage_message = `
  145. Usage: ${process.argv[1]} [options] sshkey command ...
  146. Commands :
  147. upload [file]\t\t\tUpload a new ssh-key
  148. list\t\t\t\tList the ssh-keys
  149. delete [sshkey]\t\t\tDelete the sshkey with comment [sshkey]
  150. `;
  151. this.usage_message = '\n ' + this.usage_message.substr(3).replace(/\n\t\t/g, '\n ');
  152. }
  153. list() {
  154. const blih = new Blih(this.options);
  155. blih.request({ path: '/sshkeys' }, (res) => {
  156. console.log();
  157. for (let info in res)
  158. console.log(`${info}:\n${res[info]}\n`);
  159. });
  160. }
  161. upload(params) {
  162. if (params.length < 1)
  163. return this.usage();
  164. const file = params[0];
  165. const blih = new Blih(this.options);
  166. fs.readFile(file, 'utf8', (err, data) => {
  167. if (err) {
  168. console.error(`Can't open file : ${file}`);
  169. process.exit(1);
  170. }
  171. blih.request({ path: '/sshkeys', method: 'POST', data: { sshkey: data.trim() } });
  172. });
  173. }
  174. delete(params) {
  175. if (params.length < 1)
  176. return this.usage();
  177. const blih = new Blih(this.options);
  178. blih.request({ path: `/sshkey/${params[0]}`, method: 'DELETE' });
  179. }
  180. }
  181. program.version('1.7')
  182. .option('-v, --verbose', 'Enable verbose mode [ actually no difference ]')
  183. .option('-u, --user <user>', 'Set user')
  184. .option('-b, --baseurl <url>', 'Set baseurl')
  185. .option('-t, --token <token>', 'Set token')
  186. .option('-U, --useragent <agent>', 'Set useragent');
  187. program.command('repository [params...]')
  188. .description('Manages repositories')
  189. .action(function (params) {
  190. var action = params.shift();
  191. const repo = new Repository(program);
  192. if (typeof repo[action] !== 'function')
  193. action = 'usage';
  194. repo[action](params);
  195. });
  196. program.command('sshkey [params...]')
  197. .description('Manages SSH keys')
  198. .action(function (params) {
  199. var action = params.shift();
  200. const sshkeys = new SSHKey(program);
  201. if (typeof sshkeys[action] !== 'function')
  202. action = 'usage';
  203. sshkeys[action](params);
  204. });
  205. program.command('whoami')
  206. .description('Ask who you are')
  207. .action(function() {
  208. const blih = new Blih(program);
  209. blih.request({ path: "/whoami" });
  210. });
  211. program.parse(process.argv);
  212. if (program.args.length === 0)
  213. program.help();