Epitech's Blih Repository Manager

blih.js 6.5KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248
  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\tCreate a repository named "repo"
  75. info repo\t\t\t\tGet the repository metadata
  76. getacl repo\t\t\tGet the acls set for the repository
  77. list\t\t\t\tList the repositories created
  78. setacl repo user [user...] [acl]\tSet (or remove) an acl for each "user" on "repo"
  79. \t\t\t\t\tACL format:
  80. \t\t\t\t\t\tr for read
  81. \t\t\t\t\t\tw for write
  82. \t\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. const repo = params.shift();
  127. const rights = params.pop();
  128. var users = params;
  129. for (let user of users) {
  130. blih.request({
  131. path: `/repository/${repo}/acls`,
  132. method:'POST', data: {
  133. user: user,
  134. acl: rights || ''
  135. }
  136. });
  137. }
  138. }
  139. getacl(params) {
  140. if (params.length < 1)
  141. return this.usage();
  142. const blih = new Blih(this.options);
  143. blih.request({ path: `/repository/${params[0]}/acls` }, (res) => {
  144. for (let info in res)
  145. console.log(`${info}:${res[info]}`);
  146. });
  147. }
  148. }
  149. class SSHKey extends Subcommand {
  150. constructor(options) {
  151. super(options);
  152. this.usage_message = `
  153. Usage: ${process.argv[1]} [options] sshkey command ...
  154. Commands :
  155. upload [file]\t\t\tUpload a new ssh-key
  156. list\t\t\t\tList the ssh-keys
  157. delete [sshkey]\t\t\tDelete the sshkey with comment [sshkey]
  158. `;
  159. this.usage_message = '\n ' + this.usage_message.substr(3).replace(/\n\t\t/g, '\n ');
  160. }
  161. list() {
  162. const blih = new Blih(this.options);
  163. blih.request({ path: '/sshkeys' }, (res) => {
  164. console.log();
  165. for (let info in res)
  166. console.log(`${info}:\n${res[info]}\n`);
  167. });
  168. }
  169. upload(params) {
  170. if (params.length < 1)
  171. return this.usage();
  172. const file = params[0];
  173. const blih = new Blih(this.options);
  174. fs.readFile(file, 'utf8', (err, data) => {
  175. if (err) {
  176. console.error(`Can't open file : ${file}`);
  177. process.exit(1);
  178. }
  179. blih.request({ path: '/sshkeys', method: 'POST', data: { sshkey: data.trim() } });
  180. });
  181. }
  182. delete(params) {
  183. if (params.length < 1)
  184. return this.usage();
  185. const blih = new Blih(this.options);
  186. blih.request({ path: `/sshkey/${params[0]}`, method: 'DELETE' });
  187. }
  188. }
  189. program.version('1.7')
  190. .option('-v, --verbose', 'Enable verbose mode [ actually no difference ]')
  191. .option('-u, --user <user>', 'Set user')
  192. .option('-b, --baseurl <url>', 'Set baseurl')
  193. .option('-t, --token <token>', 'Set token')
  194. .option('-U, --useragent <agent>', 'Set useragent');
  195. program.command('repository [params...]')
  196. .description('Manages repositories')
  197. .action(function (params) {
  198. var action = params.shift();
  199. const repo = new Repository(program);
  200. if (typeof repo[action] !== 'function')
  201. action = 'usage';
  202. repo[action](params);
  203. });
  204. program.command('sshkey [params...]')
  205. .description('Manages SSH keys')
  206. .action(function (params) {
  207. var action = params.shift();
  208. const sshkeys = new SSHKey(program);
  209. if (typeof sshkeys[action] !== 'function')
  210. action = 'usage';
  211. sshkeys[action](params);
  212. });
  213. program.command('whoami')
  214. .description('Ask who you are')
  215. .action(function() {
  216. const blih = new Blih(program);
  217. blih.request({ path: "/whoami" });
  218. });
  219. program.parse(process.argv);
  220. if (program.args.length === 0 || !(program.args[1] instanceof program.Command))
  221. program.help();