-
-
Notifications
You must be signed in to change notification settings - Fork 5.5k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge branch 'main' into sync-issue-pr-and-more
- Loading branch information
Showing
92 changed files
with
1,619 additions
and
304 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
--- | ||
date: "2023-08-22T14:21:00+08:00" | ||
title: "Usage: Multi-factor Authentication (MFA)" | ||
slug: "multi-factor-authentication" | ||
weight: 15 | ||
toc: false | ||
draft: false | ||
menu: | ||
sidebar: | ||
parent: "usage" | ||
name: "Multi-factor Authentication (MFA)" | ||
weight: 15 | ||
identifier: "multi-factor-authentication" | ||
--- | ||
|
||
# Multi-factor Authentication (MFA) | ||
|
||
Multi-factor Authentication (also referred to as MFA or 2FA) enhances security by requiring a time-sensitive set of credentials in addition to a password. | ||
If a password were later to be compromised, logging into Gitea will not be possible without the additional credentials and the account would remain secure. | ||
Gitea supports both TOTP (Time-based One-Time Password) tokens and FIDO-based hardware keys using the Webauthn API. | ||
|
||
MFA can be configured within the "Security" tab of the user settings page. | ||
|
||
## MFA Considerations | ||
|
||
Enabling MFA on a user does affect how the Git HTTP protocol can be used with the Git CLI. | ||
This interface does not support MFA, and trying to use a password normally will no longer be possible whilst MFA is enabled. | ||
If SSH is not an option for Git operations, an access token can be generated within the "Applications" tab of the user settings page. | ||
This access token can be used as if it were a password in order to allow the Git CLI to function over HTTP. | ||
|
||
> **Warning** - By its very nature, an access token sidesteps the security benefits of MFA. | ||
> It must be kept secure and should only be used as a last resort. | ||
The Gitea API supports providing the relevant TOTP password in the `X-Gitea-OTP` header, as described in [API Usage](development/api-usage.md). | ||
This should be used instead of an access token where possible. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,120 @@ | ||
// Copyright 2023 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package actions | ||
|
||
import ( | ||
"context" | ||
"time" | ||
|
||
"code.gitea.io/gitea/models/db" | ||
repo_model "code.gitea.io/gitea/models/repo" | ||
user_model "code.gitea.io/gitea/models/user" | ||
"code.gitea.io/gitea/modules/timeutil" | ||
webhook_module "code.gitea.io/gitea/modules/webhook" | ||
|
||
"github.com/robfig/cron/v3" | ||
) | ||
|
||
// ActionSchedule represents a schedule of a workflow file | ||
type ActionSchedule struct { | ||
ID int64 | ||
Title string | ||
Specs []string | ||
RepoID int64 `xorm:"index"` | ||
Repo *repo_model.Repository `xorm:"-"` | ||
OwnerID int64 `xorm:"index"` | ||
WorkflowID string | ||
TriggerUserID int64 | ||
TriggerUser *user_model.User `xorm:"-"` | ||
Ref string | ||
CommitSHA string | ||
Event webhook_module.HookEventType | ||
EventPayload string `xorm:"LONGTEXT"` | ||
Content []byte | ||
Created timeutil.TimeStamp `xorm:"created"` | ||
Updated timeutil.TimeStamp `xorm:"updated"` | ||
} | ||
|
||
func init() { | ||
db.RegisterModel(new(ActionSchedule)) | ||
} | ||
|
||
// GetSchedulesMapByIDs returns the schedules by given id slice. | ||
func GetSchedulesMapByIDs(ids []int64) (map[int64]*ActionSchedule, error) { | ||
schedules := make(map[int64]*ActionSchedule, len(ids)) | ||
return schedules, db.GetEngine(db.DefaultContext).In("id", ids).Find(&schedules) | ||
} | ||
|
||
// GetReposMapByIDs returns the repos by given id slice. | ||
func GetReposMapByIDs(ids []int64) (map[int64]*repo_model.Repository, error) { | ||
repos := make(map[int64]*repo_model.Repository, len(ids)) | ||
return repos, db.GetEngine(db.DefaultContext).In("id", ids).Find(&repos) | ||
} | ||
|
||
var cronParser = cron.NewParser(cron.Minute | cron.Hour | cron.Dom | cron.Month | cron.Dow | cron.Descriptor) | ||
|
||
// CreateScheduleTask creates new schedule task. | ||
func CreateScheduleTask(ctx context.Context, rows []*ActionSchedule) error { | ||
// Return early if there are no rows to insert | ||
if len(rows) == 0 { | ||
return nil | ||
} | ||
|
||
// Begin transaction | ||
ctx, committer, err := db.TxContext(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
defer committer.Close() | ||
|
||
// Loop through each schedule row | ||
for _, row := range rows { | ||
// Create new schedule row | ||
if err = db.Insert(ctx, row); err != nil { | ||
return err | ||
} | ||
|
||
// Loop through each schedule spec and create a new spec row | ||
now := time.Now() | ||
|
||
for _, spec := range row.Specs { | ||
// Parse the spec and check for errors | ||
schedule, err := cronParser.Parse(spec) | ||
if err != nil { | ||
continue // skip to the next spec if there's an error | ||
} | ||
|
||
// Insert the new schedule spec row | ||
if err = db.Insert(ctx, &ActionScheduleSpec{ | ||
RepoID: row.RepoID, | ||
ScheduleID: row.ID, | ||
Spec: spec, | ||
Next: timeutil.TimeStamp(schedule.Next(now).Unix()), | ||
}); err != nil { | ||
return err | ||
} | ||
} | ||
} | ||
|
||
// Commit transaction | ||
return committer.Commit() | ||
} | ||
|
||
func DeleteScheduleTaskByRepo(ctx context.Context, id int64) error { | ||
ctx, committer, err := db.TxContext(ctx) | ||
if err != nil { | ||
return err | ||
} | ||
defer committer.Close() | ||
|
||
if _, err := db.GetEngine(ctx).Delete(&ActionSchedule{RepoID: id}); err != nil { | ||
return err | ||
} | ||
|
||
if _, err := db.GetEngine(ctx).Delete(&ActionScheduleSpec{RepoID: id}); err != nil { | ||
return err | ||
} | ||
|
||
return committer.Commit() | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,94 @@ | ||
// Copyright 2023 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package actions | ||
|
||
import ( | ||
"context" | ||
|
||
"code.gitea.io/gitea/models/db" | ||
repo_model "code.gitea.io/gitea/models/repo" | ||
user_model "code.gitea.io/gitea/models/user" | ||
"code.gitea.io/gitea/modules/container" | ||
|
||
"xorm.io/builder" | ||
) | ||
|
||
type ScheduleList []*ActionSchedule | ||
|
||
// GetUserIDs returns a slice of user's id | ||
func (schedules ScheduleList) GetUserIDs() []int64 { | ||
ids := make(container.Set[int64], len(schedules)) | ||
for _, schedule := range schedules { | ||
ids.Add(schedule.TriggerUserID) | ||
} | ||
return ids.Values() | ||
} | ||
|
||
func (schedules ScheduleList) GetRepoIDs() []int64 { | ||
ids := make(container.Set[int64], len(schedules)) | ||
for _, schedule := range schedules { | ||
ids.Add(schedule.RepoID) | ||
} | ||
return ids.Values() | ||
} | ||
|
||
func (schedules ScheduleList) LoadTriggerUser(ctx context.Context) error { | ||
userIDs := schedules.GetUserIDs() | ||
users := make(map[int64]*user_model.User, len(userIDs)) | ||
if err := db.GetEngine(ctx).In("id", userIDs).Find(&users); err != nil { | ||
return err | ||
} | ||
for _, schedule := range schedules { | ||
if schedule.TriggerUserID == user_model.ActionsUserID { | ||
schedule.TriggerUser = user_model.NewActionsUser() | ||
} else { | ||
schedule.TriggerUser = users[schedule.TriggerUserID] | ||
} | ||
} | ||
return nil | ||
} | ||
|
||
func (schedules ScheduleList) LoadRepos() error { | ||
repoIDs := schedules.GetRepoIDs() | ||
repos, err := repo_model.GetRepositoriesMapByIDs(repoIDs) | ||
if err != nil { | ||
return err | ||
} | ||
for _, schedule := range schedules { | ||
schedule.Repo = repos[schedule.RepoID] | ||
} | ||
return nil | ||
} | ||
|
||
type FindScheduleOptions struct { | ||
db.ListOptions | ||
RepoID int64 | ||
OwnerID int64 | ||
} | ||
|
||
func (opts FindScheduleOptions) toConds() builder.Cond { | ||
cond := builder.NewCond() | ||
if opts.RepoID > 0 { | ||
cond = cond.And(builder.Eq{"repo_id": opts.RepoID}) | ||
} | ||
if opts.OwnerID > 0 { | ||
cond = cond.And(builder.Eq{"owner_id": opts.OwnerID}) | ||
} | ||
|
||
return cond | ||
} | ||
|
||
func FindSchedules(ctx context.Context, opts FindScheduleOptions) (ScheduleList, int64, error) { | ||
e := db.GetEngine(ctx).Where(opts.toConds()) | ||
if !opts.ListAll && opts.PageSize > 0 && opts.Page >= 1 { | ||
e.Limit(opts.PageSize, (opts.Page-1)*opts.PageSize) | ||
} | ||
var schedules ScheduleList | ||
total, err := e.Desc("id").FindAndCount(&schedules) | ||
return schedules, total, err | ||
} | ||
|
||
func CountSchedules(ctx context.Context, opts FindScheduleOptions) (int64, error) { | ||
return db.GetEngine(ctx).Where(opts.toConds()).Count(new(ActionSchedule)) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,50 @@ | ||
// Copyright 2023 The Gitea Authors. All rights reserved. | ||
// SPDX-License-Identifier: MIT | ||
|
||
package actions | ||
|
||
import ( | ||
"context" | ||
|
||
"code.gitea.io/gitea/models/db" | ||
repo_model "code.gitea.io/gitea/models/repo" | ||
"code.gitea.io/gitea/modules/timeutil" | ||
|
||
"github.com/robfig/cron/v3" | ||
) | ||
|
||
// ActionScheduleSpec represents a schedule spec of a workflow file | ||
type ActionScheduleSpec struct { | ||
ID int64 | ||
RepoID int64 `xorm:"index"` | ||
Repo *repo_model.Repository `xorm:"-"` | ||
ScheduleID int64 `xorm:"index"` | ||
Schedule *ActionSchedule `xorm:"-"` | ||
|
||
// Next time the job will run, or the zero time if Cron has not been | ||
// started or this entry's schedule is unsatisfiable | ||
Next timeutil.TimeStamp `xorm:"index"` | ||
// Prev is the last time this job was run, or the zero time if never. | ||
Prev timeutil.TimeStamp | ||
Spec string | ||
|
||
Created timeutil.TimeStamp `xorm:"created"` | ||
Updated timeutil.TimeStamp `xorm:"updated"` | ||
} | ||
|
||
func (s *ActionScheduleSpec) Parse() (cron.Schedule, error) { | ||
return cronParser.Parse(s.Spec) | ||
} | ||
|
||
func init() { | ||
db.RegisterModel(new(ActionScheduleSpec)) | ||
} | ||
|
||
func UpdateScheduleSpec(ctx context.Context, spec *ActionScheduleSpec, cols ...string) error { | ||
sess := db.GetEngine(ctx).ID(spec.ID) | ||
if len(cols) > 0 { | ||
sess.Cols(cols...) | ||
} | ||
_, err := sess.Update(spec) | ||
return err | ||
} |
Oops, something went wrong.