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

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113
  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(ctx, 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. db.DefaultContext,
  700. id,
  701. repo_model.FindReleasesOptions{
  702. IncludeTags: true,
  703. },
  704. )
  705. }
  706. func runRegenerateHooks(_ *cli.Context) error {
  707. ctx, cancel := installSignals()
  708. defer cancel()
  709. if err := initDB(ctx); err != nil {
  710. return err
  711. }
  712. return repo_service.SyncRepositoryHooks(graceful.GetManager().ShutdownContext())
  713. }
  714. func runRegenerateKeys(_ *cli.Context) error {
  715. ctx, cancel := installSignals()
  716. defer cancel()
  717. if err := initDB(ctx); err != nil {
  718. return err
  719. }
  720. return asymkey_model.RewriteAllPublicKeys()
  721. }
  722. func parseOAuth2Config(c *cli.Context) *oauth2.Source {
  723. var customURLMapping *oauth2.CustomURLMapping
  724. if c.IsSet("use-custom-urls") {
  725. customURLMapping = &oauth2.CustomURLMapping{
  726. TokenURL: c.String("custom-token-url"),
  727. AuthURL: c.String("custom-auth-url"),
  728. ProfileURL: c.String("custom-profile-url"),
  729. EmailURL: c.String("custom-email-url"),
  730. }
  731. } else {
  732. customURLMapping = nil
  733. }
  734. return &oauth2.Source{
  735. Provider: c.String("provider"),
  736. ClientID: c.String("key"),
  737. ClientSecret: c.String("secret"),
  738. OpenIDConnectAutoDiscoveryURL: c.String("auto-discover-url"),
  739. CustomURLMapping: customURLMapping,
  740. IconURL: c.String("icon-url"),
  741. SkipLocalTwoFA: c.Bool("skip-local-2fa"),
  742. Scopes: c.StringSlice("scopes"),
  743. RequiredClaimName: c.String("required-claim-name"),
  744. RequiredClaimValue: c.String("required-claim-value"),
  745. GroupClaimName: c.String("group-claim-name"),
  746. AdminGroup: c.String("admin-group"),
  747. RestrictedGroup: c.String("restricted-group"),
  748. }
  749. }
  750. func runAddOauth(c *cli.Context) error {
  751. ctx, cancel := installSignals()
  752. defer cancel()
  753. if err := initDB(ctx); err != nil {
  754. return err
  755. }
  756. return auth_model.CreateSource(&auth_model.Source{
  757. Type: auth_model.OAuth2,
  758. Name: c.String("name"),
  759. IsActive: true,
  760. Cfg: parseOAuth2Config(c),
  761. })
  762. }
  763. func runUpdateOauth(c *cli.Context) error {
  764. if !c.IsSet("id") {
  765. return fmt.Errorf("--id flag is missing")
  766. }
  767. ctx, cancel := installSignals()
  768. defer cancel()
  769. if err := initDB(ctx); err != nil {
  770. return err
  771. }
  772. source, err := auth_model.GetSourceByID(c.Int64("id"))
  773. if err != nil {
  774. return err
  775. }
  776. oAuth2Config := source.Cfg.(*oauth2.Source)
  777. if c.IsSet("name") {
  778. source.Name = c.String("name")
  779. }
  780. if c.IsSet("provider") {
  781. oAuth2Config.Provider = c.String("provider")
  782. }
  783. if c.IsSet("key") {
  784. oAuth2Config.ClientID = c.String("key")
  785. }
  786. if c.IsSet("secret") {
  787. oAuth2Config.ClientSecret = c.String("secret")
  788. }
  789. if c.IsSet("auto-discover-url") {
  790. oAuth2Config.OpenIDConnectAutoDiscoveryURL = c.String("auto-discover-url")
  791. }
  792. if c.IsSet("icon-url") {
  793. oAuth2Config.IconURL = c.String("icon-url")
  794. }
  795. if c.IsSet("scopes") {
  796. oAuth2Config.Scopes = c.StringSlice("scopes")
  797. }
  798. if c.IsSet("required-claim-name") {
  799. oAuth2Config.RequiredClaimName = c.String("required-claim-name")
  800. }
  801. if c.IsSet("required-claim-value") {
  802. oAuth2Config.RequiredClaimValue = c.String("required-claim-value")
  803. }
  804. if c.IsSet("group-claim-name") {
  805. oAuth2Config.GroupClaimName = c.String("group-claim-name")
  806. }
  807. if c.IsSet("admin-group") {
  808. oAuth2Config.AdminGroup = c.String("admin-group")
  809. }
  810. if c.IsSet("restricted-group") {
  811. oAuth2Config.RestrictedGroup = c.String("restricted-group")
  812. }
  813. // update custom URL mapping
  814. customURLMapping := &oauth2.CustomURLMapping{}
  815. if oAuth2Config.CustomURLMapping != nil {
  816. customURLMapping.TokenURL = oAuth2Config.CustomURLMapping.TokenURL
  817. customURLMapping.AuthURL = oAuth2Config.CustomURLMapping.AuthURL
  818. customURLMapping.ProfileURL = oAuth2Config.CustomURLMapping.ProfileURL
  819. customURLMapping.EmailURL = oAuth2Config.CustomURLMapping.EmailURL
  820. }
  821. if c.IsSet("use-custom-urls") && c.IsSet("custom-token-url") {
  822. customURLMapping.TokenURL = c.String("custom-token-url")
  823. }
  824. if c.IsSet("use-custom-urls") && c.IsSet("custom-auth-url") {
  825. customURLMapping.AuthURL = c.String("custom-auth-url")
  826. }
  827. if c.IsSet("use-custom-urls") && c.IsSet("custom-profile-url") {
  828. customURLMapping.ProfileURL = c.String("custom-profile-url")
  829. }
  830. if c.IsSet("use-custom-urls") && c.IsSet("custom-email-url") {
  831. customURLMapping.EmailURL = c.String("custom-email-url")
  832. }
  833. oAuth2Config.CustomURLMapping = customURLMapping
  834. source.Cfg = oAuth2Config
  835. return auth_model.UpdateSource(source)
  836. }
  837. func parseSMTPConfig(c *cli.Context, conf *smtp.Source) error {
  838. if c.IsSet("auth-type") {
  839. conf.Auth = c.String("auth-type")
  840. validAuthTypes := []string{"PLAIN", "LOGIN", "CRAM-MD5"}
  841. if !util.SliceContainsString(validAuthTypes, strings.ToUpper(c.String("auth-type"))) {
  842. return errors.New("Auth must be one of PLAIN/LOGIN/CRAM-MD5")
  843. }
  844. conf.Auth = c.String("auth-type")
  845. }
  846. if c.IsSet("host") {
  847. conf.Host = c.String("host")
  848. }
  849. if c.IsSet("port") {
  850. conf.Port = c.Int("port")
  851. }
  852. if c.IsSet("allowed-domains") {
  853. conf.AllowedDomains = c.String("allowed-domains")
  854. }
  855. if c.IsSet("force-smtps") {
  856. conf.ForceSMTPS = c.BoolT("force-smtps")
  857. }
  858. if c.IsSet("skip-verify") {
  859. conf.SkipVerify = c.BoolT("skip-verify")
  860. }
  861. if c.IsSet("helo-hostname") {
  862. conf.HeloHostname = c.String("helo-hostname")
  863. }
  864. if c.IsSet("disable-helo") {
  865. conf.DisableHelo = c.BoolT("disable-helo")
  866. }
  867. if c.IsSet("skip-local-2fa") {
  868. conf.SkipLocalTwoFA = c.BoolT("skip-local-2fa")
  869. }
  870. return nil
  871. }
  872. func runAddSMTP(c *cli.Context) error {
  873. ctx, cancel := installSignals()
  874. defer cancel()
  875. if err := initDB(ctx); err != nil {
  876. return err
  877. }
  878. if !c.IsSet("name") || len(c.String("name")) == 0 {
  879. return errors.New("name must be set")
  880. }
  881. if !c.IsSet("host") || len(c.String("host")) == 0 {
  882. return errors.New("host must be set")
  883. }
  884. if !c.IsSet("port") {
  885. return errors.New("port must be set")
  886. }
  887. active := true
  888. if c.IsSet("active") {
  889. active = c.BoolT("active")
  890. }
  891. var smtpConfig smtp.Source
  892. if err := parseSMTPConfig(c, &smtpConfig); err != nil {
  893. return err
  894. }
  895. // If not set default to PLAIN
  896. if len(smtpConfig.Auth) == 0 {
  897. smtpConfig.Auth = "PLAIN"
  898. }
  899. return auth_model.CreateSource(&auth_model.Source{
  900. Type: auth_model.SMTP,
  901. Name: c.String("name"),
  902. IsActive: active,
  903. Cfg: &smtpConfig,
  904. })
  905. }
  906. func runUpdateSMTP(c *cli.Context) error {
  907. if !c.IsSet("id") {
  908. return fmt.Errorf("--id flag is missing")
  909. }
  910. ctx, cancel := installSignals()
  911. defer cancel()
  912. if err := initDB(ctx); err != nil {
  913. return err
  914. }
  915. source, err := auth_model.GetSourceByID(c.Int64("id"))
  916. if err != nil {
  917. return err
  918. }
  919. smtpConfig := source.Cfg.(*smtp.Source)
  920. if err := parseSMTPConfig(c, smtpConfig); err != nil {
  921. return err
  922. }
  923. if c.IsSet("name") {
  924. source.Name = c.String("name")
  925. }
  926. if c.IsSet("active") {
  927. source.IsActive = c.BoolT("active")
  928. }
  929. source.Cfg = smtpConfig
  930. return auth_model.UpdateSource(source)
  931. }
  932. func runListAuth(c *cli.Context) error {
  933. ctx, cancel := installSignals()
  934. defer cancel()
  935. if err := initDB(ctx); err != nil {
  936. return err
  937. }
  938. authSources, err := auth_model.Sources()
  939. if err != nil {
  940. return err
  941. }
  942. flags := tabwriter.AlignRight
  943. if c.Bool("vertical-bars") {
  944. flags |= tabwriter.Debug
  945. }
  946. padChar := byte('\t')
  947. if len(c.String("pad-char")) > 0 {
  948. padChar = c.String("pad-char")[0]
  949. }
  950. // loop through each source and print
  951. w := tabwriter.NewWriter(os.Stdout, c.Int("min-width"), c.Int("tab-width"), c.Int("padding"), padChar, flags)
  952. fmt.Fprintf(w, "ID\tName\tType\tEnabled\n")
  953. for _, source := range authSources {
  954. fmt.Fprintf(w, "%d\t%s\t%s\t%t\n", source.ID, source.Name, source.Type.String(), source.IsActive)
  955. }
  956. w.Flush()
  957. return nil
  958. }
  959. func runDeleteAuth(c *cli.Context) error {
  960. if !c.IsSet("id") {
  961. return fmt.Errorf("--id flag is missing")
  962. }
  963. ctx, cancel := installSignals()
  964. defer cancel()
  965. if err := initDB(ctx); err != nil {
  966. return err
  967. }
  968. source, err := auth_model.GetSourceByID(c.Int64("id"))
  969. if err != nil {
  970. return err
  971. }
  972. return auth_service.DeleteSource(source)
  973. }