You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

admin.go 27KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112
  1. // Copyright 2016 The Gogs Authors. All rights reserved.
  2. // Copyright 2016 The Gitea Authors. All rights reserved.
  3. // SPDX-License-Identifier: MIT
  4. package cmd
  5. import (
  6. "context"
  7. "errors"
  8. "fmt"
  9. "os"
  10. "strings"
  11. "text/tabwriter"
  12. asymkey_model "code.gitea.io/gitea/models/asymkey"
  13. auth_model "code.gitea.io/gitea/models/auth"
  14. "code.gitea.io/gitea/models/db"
  15. repo_model "code.gitea.io/gitea/models/repo"
  16. user_model "code.gitea.io/gitea/models/user"
  17. "code.gitea.io/gitea/modules/git"
  18. "code.gitea.io/gitea/modules/graceful"
  19. "code.gitea.io/gitea/modules/log"
  20. pwd "code.gitea.io/gitea/modules/password"
  21. repo_module "code.gitea.io/gitea/modules/repository"
  22. "code.gitea.io/gitea/modules/setting"
  23. "code.gitea.io/gitea/modules/storage"
  24. "code.gitea.io/gitea/modules/util"
  25. auth_service "code.gitea.io/gitea/services/auth"
  26. "code.gitea.io/gitea/services/auth/source/oauth2"
  27. "code.gitea.io/gitea/services/auth/source/smtp"
  28. repo_service "code.gitea.io/gitea/services/repository"
  29. user_service "code.gitea.io/gitea/services/user"
  30. "github.com/urfave/cli"
  31. )
  32. var (
  33. // CmdAdmin represents the available admin sub-command.
  34. CmdAdmin = cli.Command{
  35. Name: "admin",
  36. Usage: "Command line interface to perform common administrative operations",
  37. Subcommands: []cli.Command{
  38. subcmdUser,
  39. subcmdRepoSyncReleases,
  40. subcmdRegenerate,
  41. subcmdAuth,
  42. subcmdSendMail,
  43. },
  44. }
  45. subcmdUser = cli.Command{
  46. Name: "user",
  47. Usage: "Modify users",
  48. Subcommands: []cli.Command{
  49. microcmdUserCreate,
  50. microcmdUserList,
  51. microcmdUserChangePassword,
  52. microcmdUserDelete,
  53. microcmdUserGenerateAccessToken,
  54. },
  55. }
  56. microcmdUserList = cli.Command{
  57. Name: "list",
  58. Usage: "List users",
  59. Action: runListUsers,
  60. Flags: []cli.Flag{
  61. cli.BoolFlag{
  62. Name: "admin",
  63. Usage: "List only admin users",
  64. },
  65. },
  66. }
  67. microcmdUserCreate = cli.Command{
  68. Name: "create",
  69. Usage: "Create a new user in database",
  70. Action: runCreateUser,
  71. Flags: []cli.Flag{
  72. cli.StringFlag{
  73. Name: "name",
  74. Usage: "Username. DEPRECATED: use username instead",
  75. },
  76. cli.StringFlag{
  77. Name: "username",
  78. Usage: "Username",
  79. },
  80. cli.StringFlag{
  81. Name: "password",
  82. Usage: "User password",
  83. },
  84. cli.StringFlag{
  85. Name: "email",
  86. Usage: "User email address",
  87. },
  88. cli.BoolFlag{
  89. Name: "admin",
  90. Usage: "User is an admin",
  91. },
  92. cli.BoolFlag{
  93. Name: "random-password",
  94. Usage: "Generate a random password for the user",
  95. },
  96. cli.BoolFlag{
  97. Name: "must-change-password",
  98. Usage: "Set this option to false to prevent forcing the user to change their password after initial login, (Default: true)",
  99. },
  100. cli.IntFlag{
  101. Name: "random-password-length",
  102. Usage: "Length of the random password to be generated",
  103. Value: 12,
  104. },
  105. cli.BoolFlag{
  106. Name: "access-token",
  107. Usage: "Generate access token for the user",
  108. },
  109. cli.BoolFlag{
  110. Name: "restricted",
  111. Usage: "Make a restricted user account",
  112. },
  113. },
  114. }
  115. microcmdUserChangePassword = cli.Command{
  116. Name: "change-password",
  117. Usage: "Change a user's password",
  118. Action: runChangePassword,
  119. Flags: []cli.Flag{
  120. cli.StringFlag{
  121. Name: "username,u",
  122. Value: "",
  123. Usage: "The user to change password for",
  124. },
  125. cli.StringFlag{
  126. Name: "password,p",
  127. Value: "",
  128. Usage: "New password to set for user",
  129. },
  130. },
  131. }
  132. microcmdUserDelete = cli.Command{
  133. Name: "delete",
  134. Usage: "Delete specific user by id, name or email",
  135. Flags: []cli.Flag{
  136. cli.Int64Flag{
  137. Name: "id",
  138. Usage: "ID of user of the user to delete",
  139. },
  140. cli.StringFlag{
  141. Name: "username,u",
  142. Usage: "Username of the user to delete",
  143. },
  144. cli.StringFlag{
  145. Name: "email,e",
  146. Usage: "Email of the user to delete",
  147. },
  148. cli.BoolFlag{
  149. Name: "purge",
  150. Usage: "Purge user, all their repositories, organizations and comments",
  151. },
  152. },
  153. Action: runDeleteUser,
  154. }
  155. microcmdUserGenerateAccessToken = cli.Command{
  156. Name: "generate-access-token",
  157. Usage: "Generate a access token for a specific user",
  158. Flags: []cli.Flag{
  159. cli.StringFlag{
  160. Name: "username,u",
  161. Usage: "Username",
  162. },
  163. cli.StringFlag{
  164. Name: "token-name,t",
  165. Usage: "Token name",
  166. Value: "gitea-admin",
  167. },
  168. cli.BoolFlag{
  169. Name: "raw",
  170. Usage: "Display only the token value",
  171. },
  172. },
  173. Action: runGenerateAccessToken,
  174. }
  175. subcmdRepoSyncReleases = cli.Command{
  176. Name: "repo-sync-releases",
  177. Usage: "Synchronize repository releases with tags",
  178. Action: runRepoSyncReleases,
  179. }
  180. subcmdRegenerate = cli.Command{
  181. Name: "regenerate",
  182. Usage: "Regenerate specific files",
  183. Subcommands: []cli.Command{
  184. microcmdRegenHooks,
  185. microcmdRegenKeys,
  186. },
  187. }
  188. microcmdRegenHooks = cli.Command{
  189. Name: "hooks",
  190. Usage: "Regenerate git-hooks",
  191. Action: runRegenerateHooks,
  192. }
  193. microcmdRegenKeys = cli.Command{
  194. Name: "keys",
  195. Usage: "Regenerate authorized_keys file",
  196. Action: runRegenerateKeys,
  197. }
  198. subcmdAuth = cli.Command{
  199. Name: "auth",
  200. Usage: "Modify external auth providers",
  201. Subcommands: []cli.Command{
  202. microcmdAuthAddOauth,
  203. microcmdAuthUpdateOauth,
  204. cmdAuthAddLdapBindDn,
  205. cmdAuthUpdateLdapBindDn,
  206. cmdAuthAddLdapSimpleAuth,
  207. cmdAuthUpdateLdapSimpleAuth,
  208. microcmdAuthAddSMTP,
  209. microcmdAuthUpdateSMTP,
  210. microcmdAuthList,
  211. microcmdAuthDelete,
  212. },
  213. }
  214. microcmdAuthList = cli.Command{
  215. Name: "list",
  216. Usage: "List auth sources",
  217. Action: runListAuth,
  218. Flags: []cli.Flag{
  219. cli.IntFlag{
  220. Name: "min-width",
  221. Usage: "Minimal cell width including any padding for the formatted table",
  222. Value: 0,
  223. },
  224. cli.IntFlag{
  225. Name: "tab-width",
  226. Usage: "width of tab characters in formatted table (equivalent number of spaces)",
  227. Value: 8,
  228. },
  229. cli.IntFlag{
  230. Name: "padding",
  231. Usage: "padding added to a cell before computing its width",
  232. Value: 1,
  233. },
  234. cli.StringFlag{
  235. Name: "pad-char",
  236. Usage: `ASCII char used for padding if padchar == '\\t', the Writer will assume that the width of a '\\t' in the formatted output is tabwidth, and cells are left-aligned independent of align_left (for correct-looking results, tabwidth must correspond to the tab width in the viewer displaying the result)`,
  237. Value: "\t",
  238. },
  239. cli.BoolFlag{
  240. Name: "vertical-bars",
  241. Usage: "Set to true to print vertical bars between columns",
  242. },
  243. },
  244. }
  245. idFlag = cli.Int64Flag{
  246. Name: "id",
  247. Usage: "ID of authentication source",
  248. }
  249. microcmdAuthDelete = cli.Command{
  250. Name: "delete",
  251. Usage: "Delete specific auth source",
  252. Flags: []cli.Flag{idFlag},
  253. Action: runDeleteAuth,
  254. }
  255. oauthCLIFlags = []cli.Flag{
  256. cli.StringFlag{
  257. Name: "name",
  258. Value: "",
  259. Usage: "Application Name",
  260. },
  261. cli.StringFlag{
  262. Name: "provider",
  263. Value: "",
  264. Usage: "OAuth2 Provider",
  265. },
  266. cli.StringFlag{
  267. Name: "key",
  268. Value: "",
  269. Usage: "Client ID (Key)",
  270. },
  271. cli.StringFlag{
  272. Name: "secret",
  273. Value: "",
  274. Usage: "Client Secret",
  275. },
  276. cli.StringFlag{
  277. Name: "auto-discover-url",
  278. Value: "",
  279. Usage: "OpenID Connect Auto Discovery URL (only required when using OpenID Connect as provider)",
  280. },
  281. cli.StringFlag{
  282. Name: "use-custom-urls",
  283. Value: "false",
  284. Usage: "Use custom URLs for GitLab/GitHub OAuth endpoints",
  285. },
  286. cli.StringFlag{
  287. Name: "custom-auth-url",
  288. Value: "",
  289. Usage: "Use a custom Authorization URL (option for GitLab/GitHub)",
  290. },
  291. cli.StringFlag{
  292. Name: "custom-token-url",
  293. Value: "",
  294. Usage: "Use a custom Token URL (option for GitLab/GitHub)",
  295. },
  296. cli.StringFlag{
  297. Name: "custom-profile-url",
  298. Value: "",
  299. Usage: "Use a custom Profile URL (option for GitLab/GitHub)",
  300. },
  301. cli.StringFlag{
  302. Name: "custom-email-url",
  303. Value: "",
  304. Usage: "Use a custom Email URL (option for GitHub)",
  305. },
  306. cli.StringFlag{
  307. Name: "icon-url",
  308. Value: "",
  309. Usage: "Custom icon URL for OAuth2 login source",
  310. },
  311. cli.BoolFlag{
  312. Name: "skip-local-2fa",
  313. Usage: "Set to true to skip local 2fa for users authenticated by this source",
  314. },
  315. cli.StringSliceFlag{
  316. Name: "scopes",
  317. Value: nil,
  318. Usage: "Scopes to request when to authenticate against this OAuth2 source",
  319. },
  320. cli.StringFlag{
  321. Name: "required-claim-name",
  322. Value: "",
  323. Usage: "Claim name that has to be set to allow users to login with this source",
  324. },
  325. cli.StringFlag{
  326. Name: "required-claim-value",
  327. Value: "",
  328. Usage: "Claim value that has to be set to allow users to login with this source",
  329. },
  330. cli.StringFlag{
  331. Name: "group-claim-name",
  332. Value: "",
  333. Usage: "Claim name providing group names for this source",
  334. },
  335. cli.StringFlag{
  336. Name: "admin-group",
  337. Value: "",
  338. Usage: "Group Claim value for administrator users",
  339. },
  340. cli.StringFlag{
  341. Name: "restricted-group",
  342. Value: "",
  343. Usage: "Group Claim value for restricted users",
  344. },
  345. }
  346. microcmdAuthUpdateOauth = cli.Command{
  347. Name: "update-oauth",
  348. Usage: "Update existing Oauth authentication source",
  349. Action: runUpdateOauth,
  350. Flags: append(oauthCLIFlags[:1], append([]cli.Flag{idFlag}, oauthCLIFlags[1:]...)...),
  351. }
  352. microcmdAuthAddOauth = cli.Command{
  353. Name: "add-oauth",
  354. Usage: "Add new Oauth authentication source",
  355. Action: runAddOauth,
  356. Flags: oauthCLIFlags,
  357. }
  358. subcmdSendMail = cli.Command{
  359. Name: "sendmail",
  360. Usage: "Send a message to all users",
  361. Action: runSendMail,
  362. Flags: []cli.Flag{
  363. cli.StringFlag{
  364. Name: "title",
  365. Usage: `a title of a message`,
  366. Value: "",
  367. },
  368. cli.StringFlag{
  369. Name: "content",
  370. Usage: "a content of a message",
  371. Value: "",
  372. },
  373. cli.BoolFlag{
  374. Name: "force,f",
  375. Usage: "A flag to bypass a confirmation step",
  376. },
  377. },
  378. }
  379. smtpCLIFlags = []cli.Flag{
  380. cli.StringFlag{
  381. Name: "name",
  382. Value: "",
  383. Usage: "Application Name",
  384. },
  385. cli.StringFlag{
  386. Name: "auth-type",
  387. Value: "PLAIN",
  388. Usage: "SMTP Authentication Type (PLAIN/LOGIN/CRAM-MD5) default PLAIN",
  389. },
  390. cli.StringFlag{
  391. Name: "host",
  392. Value: "",
  393. Usage: "SMTP Host",
  394. },
  395. cli.IntFlag{
  396. Name: "port",
  397. Usage: "SMTP Port",
  398. },
  399. cli.BoolTFlag{
  400. Name: "force-smtps",
  401. Usage: "SMTPS is always used on port 465. Set this to force SMTPS on other ports.",
  402. },
  403. cli.BoolTFlag{
  404. Name: "skip-verify",
  405. Usage: "Skip TLS verify.",
  406. },
  407. cli.StringFlag{
  408. Name: "helo-hostname",
  409. Value: "",
  410. Usage: "Hostname sent with HELO. Leave blank to send current hostname",
  411. },
  412. cli.BoolTFlag{
  413. Name: "disable-helo",
  414. Usage: "Disable SMTP helo.",
  415. },
  416. cli.StringFlag{
  417. Name: "allowed-domains",
  418. Value: "",
  419. Usage: "Leave empty to allow all domains. Separate multiple domains with a comma (',')",
  420. },
  421. cli.BoolTFlag{
  422. Name: "skip-local-2fa",
  423. Usage: "Skip 2FA to log on.",
  424. },
  425. cli.BoolTFlag{
  426. Name: "active",
  427. Usage: "This Authentication Source is Activated.",
  428. },
  429. }
  430. microcmdAuthAddSMTP = cli.Command{
  431. Name: "add-smtp",
  432. Usage: "Add new SMTP authentication source",
  433. Action: runAddSMTP,
  434. Flags: smtpCLIFlags,
  435. }
  436. microcmdAuthUpdateSMTP = cli.Command{
  437. Name: "update-smtp",
  438. Usage: "Update existing SMTP authentication source",
  439. Action: runUpdateSMTP,
  440. Flags: append(smtpCLIFlags[:1], append([]cli.Flag{idFlag}, smtpCLIFlags[1:]...)...),
  441. }
  442. )
  443. func runChangePassword(c *cli.Context) error {
  444. if err := argsSet(c, "username", "password"); err != nil {
  445. return err
  446. }
  447. ctx, cancel := installSignals()
  448. defer cancel()
  449. if err := initDB(ctx); err != nil {
  450. return err
  451. }
  452. if len(c.String("password")) < setting.MinPasswordLength {
  453. return fmt.Errorf("Password is not long enough. Needs to be at least %d", setting.MinPasswordLength)
  454. }
  455. if !pwd.IsComplexEnough(c.String("password")) {
  456. return errors.New("Password does not meet complexity requirements")
  457. }
  458. pwned, err := pwd.IsPwned(context.Background(), c.String("password"))
  459. if err != nil {
  460. return err
  461. }
  462. if pwned {
  463. return errors.New("The password you chose is on a list of stolen passwords previously exposed in public data breaches. Please try again with a different password.\nFor more details, see https://haveibeenpwned.com/Passwords")
  464. }
  465. uname := c.String("username")
  466. user, err := user_model.GetUserByName(ctx, uname)
  467. if err != nil {
  468. return err
  469. }
  470. if err = user.SetPassword(c.String("password")); err != nil {
  471. return err
  472. }
  473. if err = user_model.UpdateUserCols(ctx, user, "passwd", "passwd_hash_algo", "salt"); err != nil {
  474. return err
  475. }
  476. fmt.Printf("%s's password has been successfully updated!\n", user.Name)
  477. return nil
  478. }
  479. func runCreateUser(c *cli.Context) error {
  480. if err := argsSet(c, "email"); err != nil {
  481. return err
  482. }
  483. if c.IsSet("name") && c.IsSet("username") {
  484. return errors.New("Cannot set both --name and --username flags")
  485. }
  486. if !c.IsSet("name") && !c.IsSet("username") {
  487. return errors.New("One of --name or --username flags must be set")
  488. }
  489. if c.IsSet("password") && c.IsSet("random-password") {
  490. return errors.New("cannot set both -random-password and -password flags")
  491. }
  492. var username string
  493. if c.IsSet("username") {
  494. username = c.String("username")
  495. } else {
  496. username = c.String("name")
  497. fmt.Fprintf(os.Stderr, "--name flag is deprecated. Use --username instead.\n")
  498. }
  499. ctx, cancel := installSignals()
  500. defer cancel()
  501. if err := initDB(ctx); err != nil {
  502. return err
  503. }
  504. var password string
  505. if c.IsSet("password") {
  506. password = c.String("password")
  507. } else if c.IsSet("random-password") {
  508. var err error
  509. password, err = pwd.Generate(c.Int("random-password-length"))
  510. if err != nil {
  511. return err
  512. }
  513. fmt.Printf("generated random password is '%s'\n", password)
  514. } else {
  515. return errors.New("must set either password or random-password flag")
  516. }
  517. // always default to true
  518. changePassword := true
  519. // If this is the first user being created.
  520. // Take it as the admin and don't force a password update.
  521. if n := user_model.CountUsers(nil); n == 0 {
  522. changePassword = false
  523. }
  524. if c.IsSet("must-change-password") {
  525. changePassword = c.Bool("must-change-password")
  526. }
  527. restricted := util.OptionalBoolNone
  528. if c.IsSet("restricted") {
  529. restricted = util.OptionalBoolOf(c.Bool("restricted"))
  530. }
  531. u := &user_model.User{
  532. Name: username,
  533. Email: c.String("email"),
  534. Passwd: password,
  535. IsAdmin: c.Bool("admin"),
  536. MustChangePassword: changePassword,
  537. }
  538. overwriteDefault := &user_model.CreateUserOverwriteOptions{
  539. IsActive: util.OptionalBoolTrue,
  540. IsRestricted: restricted,
  541. }
  542. if err := user_model.CreateUser(u, overwriteDefault); err != nil {
  543. return fmt.Errorf("CreateUser: %w", err)
  544. }
  545. if c.Bool("access-token") {
  546. t := &auth_model.AccessToken{
  547. Name: "gitea-admin",
  548. UID: u.ID,
  549. }
  550. if err := auth_model.NewAccessToken(t); err != nil {
  551. return err
  552. }
  553. fmt.Printf("Access token was successfully created... %s\n", t.Token)
  554. }
  555. fmt.Printf("New user '%s' has been successfully created!\n", username)
  556. return nil
  557. }
  558. func runListUsers(c *cli.Context) error {
  559. ctx, cancel := installSignals()
  560. defer cancel()
  561. if err := initDB(ctx); err != nil {
  562. return err
  563. }
  564. users, err := user_model.GetAllUsers()
  565. if err != nil {
  566. return err
  567. }
  568. w := tabwriter.NewWriter(os.Stdout, 5, 0, 1, ' ', 0)
  569. if c.IsSet("admin") {
  570. fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\n")
  571. for _, u := range users {
  572. if u.IsAdmin {
  573. fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", u.ID, u.Name, u.Email, u.IsActive)
  574. }
  575. }
  576. } else {
  577. twofa := user_model.UserList(users).GetTwoFaStatus()
  578. fmt.Fprintf(w, "ID\tUsername\tEmail\tIsActive\tIsAdmin\t2FA\n")
  579. for _, u := range users {
  580. fmt.Fprintf(w, "%d\t%s\t%s\t%t\t%t\t%t\n", u.ID, u.Name, u.Email, u.IsActive, u.IsAdmin, twofa[u.ID])
  581. }
  582. }
  583. w.Flush()
  584. return nil
  585. }
  586. func runDeleteUser(c *cli.Context) error {
  587. if !c.IsSet("id") && !c.IsSet("username") && !c.IsSet("email") {
  588. return fmt.Errorf("You must provide the id, username or email of a user to delete")
  589. }
  590. ctx, cancel := installSignals()
  591. defer cancel()
  592. if err := initDB(ctx); err != nil {
  593. return err
  594. }
  595. if err := storage.Init(); err != nil {
  596. return err
  597. }
  598. var err error
  599. var user *user_model.User
  600. if c.IsSet("email") {
  601. user, err = user_model.GetUserByEmail(c.String("email"))
  602. } else if c.IsSet("username") {
  603. user, err = user_model.GetUserByName(ctx, c.String("username"))
  604. } else {
  605. user, err = user_model.GetUserByID(c.Int64("id"))
  606. }
  607. if err != nil {
  608. return err
  609. }
  610. if c.IsSet("username") && user.LowerName != strings.ToLower(strings.TrimSpace(c.String("username"))) {
  611. return fmt.Errorf("The user %s who has email %s does not match the provided username %s", user.Name, c.String("email"), c.String("username"))
  612. }
  613. if c.IsSet("id") && user.ID != c.Int64("id") {
  614. return fmt.Errorf("The user %s does not match the provided id %d", user.Name, c.Int64("id"))
  615. }
  616. return user_service.DeleteUser(ctx, user, c.Bool("purge"))
  617. }
  618. func runGenerateAccessToken(c *cli.Context) error {
  619. if !c.IsSet("username") {
  620. return fmt.Errorf("You must provide the username to generate a token for them")
  621. }
  622. ctx, cancel := installSignals()
  623. defer cancel()
  624. if err := initDB(ctx); err != nil {
  625. return err
  626. }
  627. user, err := user_model.GetUserByName(ctx, c.String("username"))
  628. if err != nil {
  629. return err
  630. }
  631. t := &auth_model.AccessToken{
  632. Name: c.String("token-name"),
  633. UID: user.ID,
  634. }
  635. if err := auth_model.NewAccessToken(t); err != nil {
  636. return err
  637. }
  638. if c.Bool("raw") {
  639. fmt.Printf("%s\n", t.Token)
  640. } else {
  641. fmt.Printf("Access token was successfully created: %s\n", t.Token)
  642. }
  643. return nil
  644. }
  645. func runRepoSyncReleases(_ *cli.Context) error {
  646. ctx, cancel := installSignals()
  647. defer cancel()
  648. if err := initDB(ctx); err != nil {
  649. return err
  650. }
  651. log.Trace("Synchronizing repository releases (this may take a while)")
  652. for page := 1; ; page++ {
  653. repos, count, err := repo_model.SearchRepositoryByName(ctx, &repo_model.SearchRepoOptions{
  654. ListOptions: db.ListOptions{
  655. PageSize: repo_model.RepositoryListDefaultPageSize,
  656. Page: page,
  657. },
  658. Private: true,
  659. })
  660. if err != nil {
  661. return fmt.Errorf("SearchRepositoryByName: %w", err)
  662. }
  663. if len(repos) == 0 {
  664. break
  665. }
  666. log.Trace("Processing next %d repos of %d", len(repos), count)
  667. for _, repo := range repos {
  668. log.Trace("Synchronizing repo %s with path %s", repo.FullName(), repo.RepoPath())
  669. gitRepo, err := git.OpenRepository(ctx, repo.RepoPath())
  670. if err != nil {
  671. log.Warn("OpenRepository: %v", err)
  672. continue
  673. }
  674. oldnum, err := getReleaseCount(repo.ID)
  675. if err != nil {
  676. log.Warn(" GetReleaseCountByRepoID: %v", err)
  677. }
  678. log.Trace(" currentNumReleases is %d, running SyncReleasesWithTags", oldnum)
  679. if err = repo_module.SyncReleasesWithTags(repo, gitRepo); err != nil {
  680. log.Warn(" SyncReleasesWithTags: %v", err)
  681. gitRepo.Close()
  682. continue
  683. }
  684. count, err = getReleaseCount(repo.ID)
  685. if err != nil {
  686. log.Warn(" GetReleaseCountByRepoID: %v", err)
  687. gitRepo.Close()
  688. continue
  689. }
  690. log.Trace(" repo %s releases synchronized to tags: from %d to %d",
  691. repo.FullName(), oldnum, count)
  692. gitRepo.Close()
  693. }
  694. }
  695. return nil
  696. }
  697. func getReleaseCount(id int64) (int64, error) {
  698. return repo_model.GetReleaseCountByRepoID(
  699. id,
  700. repo_model.FindReleasesOptions{
  701. IncludeTags: true,
  702. },
  703. )
  704. }
  705. func runRegenerateHooks(_ *cli.Context) error {
  706. ctx, cancel := installSignals()
  707. defer cancel()
  708. if err := initDB(ctx); err != nil {
  709. return err
  710. }
  711. return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
  712. }
  713. func runRegenerateKeys(_ *cli.Context) error {
  714. ctx, cancel := installSignals()
  715. defer cancel()
  716. if err := initDB(ctx); err != nil {
  717. return err
  718. }
  719. return asymkey_model.RewriteAllPublicKeys()
  720. }
  721. func parseOAuth2Config(c *cli.Context) *oauth2.Source {
  722. var customURLMapping *oauth2.CustomURLMapping
  723. if c.IsSet("use-custom-urls") {
  724. customURLMapping = &oauth2.CustomURLMapping{
  725. TokenURL: c.String("custom-token-url"),
  726. AuthURL: c.String("custom-auth-url"),
  727. ProfileURL: c.String("custom-profile-url"),
  728. EmailURL: c.String("custom-email-url"),
  729. }
  730. } else {
  731. customURLMapping = nil
  732. }
  733. return &oauth2.Source{
  734. Provider: c.String("provider"),
  735. ClientID: c.String("key"),
  736. ClientSecret: c.String("secret"),
  737. OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
  738. CustomURLMapping: customURLMapping,
  739. IconURL: c.String("icon-url"),
  740. SkipLocalTwoFA: c.Bool("skip-local-2fa"),
  741. Scopes: c.StringSlice("scopes"),
  742. RequiredClaimName: c.String("required-claim-name"),
  743. RequiredClaimValue: c.String("required-claim-value"),
  744. GroupClaimName: c.String("group-claim-name"),
  745. AdminGroup: c.String("admin-group"),
  746. RestrictedGroup: c.String("restricted-group"),
  747. }
  748. }
  749. func runAddOauth(c *cli.Context) error {
  750. ctx, cancel := installSignals()
  751. defer cancel()
  752. if err := initDB(ctx); err != nil {
  753. return err
  754. }
  755. return auth_model.CreateSource(&auth_model.Source{
  756. Type: auth_model.OAuth2,
  757. Name: c.String("name"),
  758. IsActive: true,
  759. Cfg: parseOAuth2Config(c),
  760. })
  761. }
  762. func runUpdateOauth(c *cli.Context) error {
  763. if !c.IsSet("id") {
  764. return fmt.Errorf("--id flag is missing")
  765. }
  766. ctx, cancel := installSignals()
  767. defer cancel()
  768. if err := initDB(ctx); err != nil {
  769. return err
  770. }
  771. source, err := auth_model.GetSourceByID(c.Int64("id"))
  772. if err != nil {
  773. return err
  774. }
  775. oAuth2Config := source.Cfg.(*oauth2.Source)
  776. if c.IsSet("name") {
  777. source.Name = c.String("name")
  778. }
  779. if c.IsSet("provider") {
  780. oAuth2Config.Provider = c.String("provider")
  781. }
  782. if c.IsSet("key") {
  783. oAuth2Config.ClientID = c.String("key")
  784. }
  785. if c.IsSet("secret") {
  786. oAuth2Config.ClientSecret = c.String("secret")
  787. }
  788. if c.IsSet("auto-discover-url") {
  789. oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
  790. }
  791. if c.IsSet("icon-url") {
  792. oAuth2Config.IconURL = c.String("icon-url")
  793. }
  794. if c.IsSet("scopes") {
  795. oAuth2Config.Scopes = c.StringSlice("scopes")
  796. }
  797. if c.IsSet("required-claim-name") {
  798. oAuth2Config.RequiredClaimName = c.String("required-claim-name")
  799. }
  800. if c.IsSet("required-claim-value") {
  801. oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
  802. }
  803. if c.IsSet("group-claim-name") {
  804. oAuth2Config.GroupClaimName = c.String("group-claim-name")
  805. }
  806. if c.IsSet("admin-group") {
  807. oAuth2Config.AdminGroup = c.String("admin-group")
  808. }
  809. if c.IsSet("restricted-group") {
  810. oAuth2Config.RestrictedGroup = c.String("restricted-group")
  811. }
  812. // update custom URL mapping
  813. customURLMapping := &oauth2.CustomURLMapping{}
  814. if oAuth2Config.CustomURLMapping != nil {
  815. customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
  816. customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL
  817. customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL
  818. customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL
  819. }
  820. if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") {
  821. customURLMapping.TokenURL = c.String("custom-token-url")
  822. }
  823. if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") {
  824. customURLMapping.AuthURL = c.String("custom-auth-url")
  825. }
  826. if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") {
  827. customURLMapping.ProfileURL = c.String("custom-profile-url")
  828. }
  829. if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") {
  830. customURLMapping.EmailURL = c.String("custom-email-url")
  831. }
  832. oAuth2Config.CustomURLMapping = customURLMapping
  833. source.Cfg = oAuth2Config
  834. return auth_model.UpdateSource(source)
  835. }
  836. func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
  837. if c.IsSet("auth-type") {
  838. conf.Auth = c.String("auth-type")
  839. validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
  840. if !contains(validAuthTypes, strings.ToUpper(c.String("auth-type"))) {
  841. return errors.New("Auth must be one of PLAIN/LOGIN/CRAM-MD5")
  842. }
  843. conf.Auth = c.String("auth-type")
  844. }
  845. if c.IsSet("host") {
  846. conf.Host = c.String("host")
  847. }
  848. if c.IsSet("port") {
  849. conf.Port = c.Int("port")
  850. }
  851. if c.IsSet("allowed-domains") {
  852. conf.AllowedDomains = c.String("allowed-domains")
  853. }
  854. if c.IsSet("force-smtps") {
  855. conf.ForceSMTPS = c.BoolT("force-smtps")
  856. }
  857. if c.IsSet("skip-verify") {
  858. conf.SkipVerify = c.BoolT("skip-verify")
  859. }
  860. if c.IsSet("helo-hostname") {
  861. conf.HeloHostname = c.String("helo-hostname")
  862. }
  863. if c.IsSet("disable-helo") {
  864. conf.DisableHelo = c.BoolT("disable-helo")
  865. }
  866. if c.IsSet("skip-local-2fa") {
  867. conf.SkipLocalTwoFA = c.BoolT("skip-local-2fa")
  868. }
  869. return nil
  870. }
  871. func runAddSMTP(c *cli.Context) error {
  872. ctx, cancel := installSignals()
  873. defer cancel()
  874. if err := initDB(ctx); err != nil {
  875. return err
  876. }
  877. if !c.IsSet("name") || len(c.String("name")) == 0 {
  878. return errors.New("name must be set")
  879. }
  880. if !c.IsSet("host") || len(c.String("host")) == 0 {
  881. return errors.New("host must be set")
  882. }
  883. if !c.IsSet("port") {
  884. return errors.New("port must be set")
  885. }
  886. active := true
  887. if c.IsSet("active") {
  888. active = c.BoolT("active")
  889. }
  890. var smtpConfig smtp.Source
  891. if err := parseSMTPConfig(c, &smtpConfig); err != nil {
  892. return err
  893. }
  894. // If not set default to PLAIN
  895. if len(smtpConfig.Auth) == 0 {
  896. smtpConfig.Auth = "PLAIN"
  897. }
  898. return auth_model.CreateSource(&auth_model.Source{
  899. Type: auth_model.SMTP,
  900. Name: c.String("name"),
  901. IsActive: active,
  902. Cfg: &smtpConfig,
  903. })
  904. }
  905. func runUpdateSMTP(c *cli.Context) error {
  906. if !c.IsSet("id") {
  907. return fmt.Errorf("--id flag is missing")
  908. }
  909. ctx, cancel := installSignals()
  910. defer cancel()
  911. if err := initDB(ctx); err != nil {
  912. return err
  913. }
  914. source, err := auth_model.GetSourceByID(c.Int64("id"))
  915. if err != nil {
  916. return err
  917. }
  918. smtpConfig := source.Cfg.(*smtp.Source)
  919. if err := parseSMTPConfig(c, smtpConfig); err != nil {
  920. return err
  921. }
  922. if c.IsSet("name") {
  923. source.Name = c.String("name")
  924. }
  925. if c.IsSet("active") {
  926. source.IsActive = c.BoolT("active")
  927. }
  928. source.Cfg = smtpConfig
  929. return auth_model.UpdateSource(source)
  930. }
  931. func runListAuth(c *cli.Context) error {
  932. ctx, cancel := installSignals()
  933. defer cancel()
  934. if err := initDB(ctx); err != nil {
  935. return err
  936. }
  937. authSources, err := auth_model.Sources()
  938. if err != nil {
  939. return err
  940. }
  941. flags := tabwriter.AlignRight
  942. if c.Bool("vertical-bars") {
  943. flags |= tabwriter.Debug
  944. }
  945. padChar := byte('\t')
  946. if len(c.String("pad-char")) > 0 {
  947. padChar = c.String("pad-char")[0]
  948. }
  949. // loop through each source and print
  950. w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags)
  951. fmt.Fprintf(w, "ID\tName\tType\tEnabled\n")
  952. for _, source := range authSources {
  953. fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive)
  954. }
  955. w.Flush()
  956. return nil
  957. }
  958. func runDeleteAuth(c *cli.Context) error {
  959. if !c.IsSet("id") {
  960. return fmt.Errorf("--id flag is missing")
  961. }
  962. ctx, cancel := installSignals()
  963. defer cancel()
  964. if err := initDB(ctx); err != nil {
  965. return err
  966. }
  967. source, err := auth_model.GetSourceByID(c.Int64("id"))
  968. if err != nil {
  969. return err
  970. }
  971. return auth_service.DeleteSource(source)
  972. }