diff --git a/models/user/user.go b/models/user/user.go index f93fba8ae0..d5c4833cde 100644 --- a/models/user/user.go +++ b/models/user/user.go @@ -408,6 +408,10 @@ func (u *User) IsIndividual() bool { return u.Type == UserTypeIndividual } +func (u *User) IsUser() bool { + return u.Type == UserTypeIndividual || u.Type == UserTypeBot +} + // IsBot returns whether or not the user is of type bot func (u *User) IsBot() bool { return u.Type == UserTypeBot diff --git a/modules/git/git.go b/modules/git/git.go index 05ca260855..a19dd7771b 100644 --- a/modules/git/git.go +++ b/modules/git/git.go @@ -111,12 +111,12 @@ func SetExecutablePath(path string) error { func ensureGitVersion() error { if !DefaultFeatures().CheckVersionAtLeast(RequiredVersion) { - moreHint := "get git: https://git-scm.com/download/" + moreHint := "get git: https://git-scm.com/downloads" if runtime.GOOS == "linux" { // there are a lot of CentOS/RHEL users using old git, so we add a special hint for them if _, err := os.Stat("/etc/redhat-release"); err == nil { // ius.io is the recommended official(git-scm.com) method to install git - moreHint = "get git: https://git-scm.com/download/linux and https://ius.io" + moreHint = "get git: https://git-scm.com/downloads/linux and https://ius.io" } } return fmt.Errorf("installed git version %q is not supported, Gitea requires git version >= %q, %s", DefaultFeatures().gitVersion.Original(), RequiredVersion, moreHint) diff --git a/options/locale/locale_fr-FR.ini b/options/locale/locale_fr-FR.ini index e64c85b7a4..f58ce74564 100644 --- a/options/locale/locale_fr-FR.ini +++ b/options/locale/locale_fr-FR.ini @@ -580,6 +580,8 @@ lang_select_error=Sélectionnez une langue dans la liste. username_been_taken=Le nom d'utilisateur est déjà pris. username_change_not_local_user=Les utilisateurs non-locaux n'ont pas le droit de modifier leur nom d'utilisateur. +change_username_disabled=Le changement de nom d’utilisateur est désactivé. +change_full_name_disabled=Le changement de nom complet est désactivé. username_has_not_been_changed=Le nom d'utilisateur n'a pas été modifié repo_name_been_taken=Ce nom de dépôt est déjà utilisé. repository_force_private=Force Private est activé : les dépôts privés ne peuvent pas être rendus publics. @@ -1039,6 +1041,7 @@ issue_labels_helper=Sélectionner un jeu de label. license=Licence license_helper=Sélectionner une licence license_helper_desc=Une licence réglemente ce que les autres peuvent ou ne peuvent pas faire avec votre code. Vous ne savez pas laquelle est la bonne pour votre projet ? Comment choisir une licence. +multiple_licenses=Licences multiples object_format=Format d'objet object_format_helper=Format d’objet pour ce dépôt. Ne peut être modifié plus tard. SHA1 est le plus compatible. readme=LISEZMOI @@ -1834,7 +1837,7 @@ pulls.is_empty=Les changements sur cette branche sont déjà sur la branche cibl pulls.required_status_check_failed=Certains contrôles requis n'ont pas réussi. pulls.required_status_check_missing=Certains contrôles requis sont manquants. pulls.required_status_check_administrator=En tant qu'administrateur, vous pouvez toujours fusionner cette requête de pull. -pulls.blocked_by_approvals=Cette demande d'ajout n’est pas suffisamment approuvée. %d approbations obtenues sur %d. +pulls.blocked_by_approvals=Cette demande d’ajout n’est pas suffisamment approuvée. %d approbations obtenues sur %d. pulls.blocked_by_approvals_whitelisted=Cette demande d’ajout n’a pas encore assez d’approbations. %d sur %d approbations de la part des utilisateurs ou équipes sur la liste autorisée. pulls.blocked_by_rejection=Cette demande d’ajout nécessite des corrections sollicitées par un évaluateur officiel. pulls.blocked_by_official_review_requests=Cette demande d’ajout a des sollicitations officielles d’évaluation. @@ -2940,6 +2943,7 @@ dashboard.start_schedule_tasks=Démarrer les tâches planifiées dashboard.sync_branch.started=Début de la synchronisation des branches dashboard.sync_tag.started=Synchronisation des étiquettes dashboard.rebuild_issue_indexer=Reconstruire l’indexeur des tickets +dashboard.sync_repo_licenses=Synchroniser les licences du dépôt users.user_manage_panel=Gestion du compte utilisateur users.new_account=Créer un compte diff --git a/options/locale/locale_ga-IE.ini b/options/locale/locale_ga-IE.ini index 82209b1b11..5ccbc4315f 100644 --- a/options/locale/locale_ga-IE.ini +++ b/options/locale/locale_ga-IE.ini @@ -2890,17 +2890,167 @@ dashboard.delete_generated_repository_avatars=Scrios abhatáranna stórtha ginte dashboard.sync_repo_branches=Sync brainsí caillte ó shonraí git go bunachair sonraí dashboard.sync_repo_tags=Clibeanna sioncraigh ó shonraí git go bunachar sonraí dashboard.update_mirrors=Scátháin a nuashonrú +dashboard.repo_health_check=Seiceáil sláinte gach stóras +dashboard.check_repo_stats=Seiceáil gach staitisticí stórais +dashboard.archive_cleanup=Scrios sean-chartlanna stórais +dashboard.deleted_branches_cleanup=Brainsí scriosta a ghlanadh +dashboard.update_migration_poster_id=Nuashonraigh ID póstaer imir +dashboard.git_gc_repos=Bailíonn truflais gach stórais +dashboard.resync_all_sshkeys=Nuashonraigh an comhad '.ssh/authorized_keys' le heochracha Gitea SSH. +dashboard.resync_all_sshprincipals=Nuashonraigh an comhad '.ssh/authorized_principals' le príomhphrionsabail Gitea SSH. +dashboard.resync_all_hooks=Athshioncrónaigh crúcaí réamhfhála, nuashonraithe agus iar-fhála na stórtha go léir. +dashboard.reinit_missing_repos=Aththosaigh gach stórais Git atá in easnamh a bhfuil taifid ann dóibh +dashboard.sync_external_users=Sioncrónaigh sonraí úsáideoirí seachtracha +dashboard.cleanup_hook_task_table=Tábla hook_task glantacháin +dashboard.cleanup_packages=Pacáistí glanta in éag +dashboard.cleanup_actions=Gníomhaíochtaí glanta in éag acmhainní +dashboard.server_uptime=Aga fónaimh Freastalaí +dashboard.current_goroutine=Goroutines Reatha +dashboard.current_memory_usage=Úsáid Cuimhne Reatha +dashboard.total_memory_allocated=Cuimhne Iomlán Leithdháilte +dashboard.memory_obtained=Cuimhne Faighte +dashboard.pointer_lookup_times=Amanna Cuardaigh Pointeora +dashboard.memory_allocate_times=Leithdháiltí Cuimhne +dashboard.memory_free_times=Saorálann Cuimhne +dashboard.current_heap_usage=Úsáid Charn Reatha +dashboard.heap_memory_obtained=Cuimhne Charn Faighte +dashboard.heap_memory_idle=Díomhaoin Cuimhne Carn +dashboard.heap_memory_in_use=Cuimhne Carm In Úsáid +dashboard.heap_memory_released=Cuimhne Carn Eisithe +dashboard.heap_objects=Cuspóirí Carn +dashboard.bootstrap_stack_usage=Úsáid Staca Bootstrap +dashboard.stack_memory_obtained=Cuimhne Staca Faighte +dashboard.mspan_structures_usage=Úsáid Struchtúir MSpan +dashboard.mspan_structures_obtained=Struchtúir MSpan a Faightear +dashboard.mcache_structures_usage=Úsáid Struchtúir MCache +dashboard.mcache_structures_obtained=Struchtúir MCache a Faightear +dashboard.profiling_bucket_hash_table_obtained=Tábla Hash Buicéad Próifílithe a Faightear +dashboard.gc_metadata_obtained=Meiteashonraí GC faighte +dashboard.other_system_allocation_obtained=Leithdháileadh Córais Eile a Fuarthas +dashboard.next_gc_recycle=Athchúrsáil GC Eile +dashboard.last_gc_time=Ó Am Deiridh GC +dashboard.total_gc_time=Sos Iomlán GC +dashboard.total_gc_pause=Sos Iomlán GC +dashboard.last_gc_pause=Sos GC Deireanach +dashboard.gc_times=Amanna GC +dashboard.delete_old_actions=Scrios gach sean-ghníomhaíocht ón mbunachar +dashboard.delete_old_actions.started=Scrios na sean-ghníomhaíocht go léir ón mbunachar sonraí tosaithe. +dashboard.update_checker=Seiceoir nuashonraithe +dashboard.delete_old_system_notices=Scrios gach seanfhógra córais ón mbunachar sonraí +dashboard.gc_lfs=Bailigh truflais meta rudaí LFS +dashboard.stop_zombie_tasks=Stad gníomhartha tascanna zombie +dashboard.stop_endless_tasks=Stad gníomhartha tascanna gan deireadh +dashboard.cancel_abandoned_jobs=Cealaigh gníomhartha poist tréigthe +dashboard.start_schedule_tasks=Tosaigh tascanna sceideal gníom +dashboard.sync_branch.started=Thosaigh Brainsí Sioncronú +dashboard.sync_tag.started=Clibeanna Thosaigh Sioncronú +dashboard.rebuild_issue_indexer=Atógáil innéacsóir eisiúna dashboard.sync_repo_licenses=Sioncronaigh ceadúnais repo +users.user_manage_panel=Bainistíocht Cuntas Úsáideora +users.new_account=Cruthaigh cuntas Úsáideora +users.name=Ainm úsáideora users.full_name=Ainm Iomlán +users.activated=Gníomhachtaithe +users.admin=Riarachán +users.restricted=Srianta +users.reserved=In áirithe +users.bot=Bota +users.remote=Iargúlta +users.2fa=2FA +users.repos=Stórais +users.created=Cruthaithe +users.last_login=Sínigh Isteach Deiridh +users.never_login=Ná Sínigh Isteach riamh +users.send_register_notify=Seol Fógra um Chlárú Úsáideora +users.new_success=Tá an cuntas úsáideora "%s" cruthaithe. +users.edit=Eagar +users.auth_source=Foinse Fíordheimhnithe +users.local=Áitiúil +users.auth_login_name=Ainm Síniú Isteach Fíordheimhnithe +users.password_helper=Fág an pasfhocal folamh chun é a choinneáil gan athrú. +users.update_profile_success=Nuashonraíodh an cuntas úsáideora. +users.edit_account=Cuir Cuntas Úsáideora in Eagar +users.max_repo_creation=Uasmhéid Stóras +users.max_repo_creation_desc=(Cuir isteach -1 chun an teorainn réamhshocraithe domhanda a úsáid.) +users.is_activated=Gníomhachtaítear Cuntas Úsáideora +users.prohibit_login=Díchumasaigh Síniú Isteach +users.is_admin=Is Riarthóir +users.is_restricted=Is Srianta +users.allow_git_hook=Féadfaidh Git Hooks a Chruthú +users.allow_git_hook_tooltip=Déantar Git Hooks a fhorghníomhú mar úsáideoir OS a ritheann Gitea agus beidh an leibhéal céanna rochtana óstaigh aige. Mar thoradh air sin, is féidir le húsáideoirí a bhfuil an phribhléid speisialta Git Hook seo acu rochtain a fháil ar gach stór Gitea agus iad a mhodhnú chomh maith leis an mbunachar sonraí a úsáideann Gitea. Dá bharr sin tá siad in ann pribhléidí riarthóra Gitea a fháil freisin. +users.allow_import_local=Is féidir Stórais Áitiúla a Allmhairiú +users.allow_create_organization=Is féidir Eagraíochtaí a Chruthú +users.update_profile=Nuashonraigh Cuntas Úsáideora +users.delete_account=Scrios Cuntas Úsáide +users.cannot_delete_self=Ní féidir leat tú féin a scriosadh +users.still_own_repo=Tá stórais amháin nó níos mó fós ag an úsáideoir seo. Scrios nó aistrigh na stórais seo ar dtús. +users.still_has_org=Is ball d'eagraíocht é an t-úsáideoir seo. Bain an t-úsáideoir ó aon eagraíochtaí ar dtús. +users.purge=Úsáideoir a Ghlanadh +users.purge_help=Scrios go héigeantach úsáideoir agus aon stórais, eagraíochtaí agus pacáistí atá faoi úinéireacht an úsáideora. Scriosfar gach trácht freisin. +users.still_own_packages=Tá pacáiste amháin nó níos mó fós ag an úsáideoir seo, scrios na pacáistí seo ar dtús. +users.deletion_success=Scriosadh an cuntas úsáideora. +users.reset_2fa=Athshocraigh 2FA +users.list_status_filter.menu_text=Scagaire +users.list_status_filter.reset=Athshocraigh users.list_status_filter.is_active=Gníomhach +users.list_status_filter.not_active=Neamhghníomhach +users.list_status_filter.is_admin=Riarachán +users.list_status_filter.not_admin=Ní Riarachán +users.list_status_filter.is_restricted=Srianta +users.list_status_filter.not_restricted=Gan Srian +users.list_status_filter.is_prohibit_login=Cosc ar Logáil Isteach +users.list_status_filter.not_prohibit_login=Ceadaigh Logáil isteach +users.list_status_filter.is_2fa_enabled=2FA Cumasaithe +users.list_status_filter.not_2fa_enabled=2FA faoi mhíchumas +users.details=Sonraí Úsáideora +emails.email_manage_panel=Bainistíocht Ríomhphost Úsáideoir +emails.primary=Bunscoile +emails.activated=Gníomhachtaithe +emails.filter_sort.email=Ríomhphost +emails.filter_sort.email_reverse=Ríomhphost (droim ar ais) +emails.filter_sort.name=Ainm Úsáideora +emails.filter_sort.name_reverse=Ainm Úsáideora (droim ar ais) +emails.updated=Nuashonraíodh an ríomhphost +emails.not_updated=Theip ar an seoladh ríomhphoist iarrtha a nuashonrú: %v +emails.duplicate_active=Tá an seoladh ríomhphoist seo gníomhach cheana féin d'úsáideoir difriúil. +emails.change_email_header=Nuashonraigh Airíonna Ríomhphoist +emails.change_email_text=An bhfuil tú cinnte gur mhaith leat an seoladh ríomhphoist seo a nuashonrú? +emails.delete=Scrios Ríomhphost +emails.delete_desc=An bhfuil tú cinnte gur mhaith leat an seoladh ríomhphoist seo a scriosadh? +emails.deletion_success=Tá an seoladh ríomhphoist scriosta. +emails.delete_primary_email_error=Ní féidir leat an ríomhphost príomhúil a scriosadh. +orgs.org_manage_panel=Bainistíocht Eagraíochta +orgs.name=Ainm orgs.teams=Foirne +orgs.members=Comhaltaí +orgs.new_orga=Eagraíocht Nua +repos.repo_manage_panel=Bainistíocht Stórais +repos.unadopted=Stórais Neamhghlactha +repos.unadopted.no_more=Níor aimsíodh níos mó stórais neamhghlactha repos.owner=Úinéir +repos.name=Ainm +repos.private=Príobháideach +repos.issues=Saincheisteanna +repos.size=Méid +repos.lfs_size=Méid LFS +packages.package_manage_panel=Bainistíocht Pacáiste +packages.total_size=Méid Iomlán: %s +packages.unreferenced_size=Méid gan tagairt: %s +packages.cleanup=Glan suas sonraí in éag +packages.cleanup.success=Glanadh suas sonraí in éag go rathúil packages.owner=Úinéir +packages.creator=Cruthaitheoir +packages.name=Ainm +packages.version=Leagan +packages.type=Cineál +packages.repository=Stóráil +packages.size=Méid +packages.published=Foilsithe defaulthooks=Réamhshocraithe Crúcaí Gréasán defaulthooks.desc=Déanann Crúcaí Gréasán iarratais HTTP POST go huathoibríoch chuig freastalaí nuair a chuireann imeachtaí áirithe Gitea tús. Is mainneachtainí iad na cuacha gréasáin a shainítear anseo agus déanfar iad a chóipeáil isteach i ngach stórais nua. Léigh tuilleadh sa treoir chúca Crúcaí Gréasán. @@ -2912,49 +3062,375 @@ systemhooks.desc=Déanann Crúcaí Gréasán iarratais HTTP POST go huathoibrío systemhooks.add_webhook=Cuir Crúca Gréasán Córas leis systemhooks.update_webhook=Nuashonraigh Córas Crúca Gréasán +auths.auth_manage_panel=Bainistiú Foinse Fíordheimhnithe +auths.new=Cuir Foinse Fíordheimhni +auths.name=Ainm +auths.type=Cineál +auths.enabled=Cumasaithe +auths.syncenabled=Cumasaigh Sioncrónú Úsáideora auths.updated=Nuashonraithe +auths.auth_type=Cineál Fíordheimhnithe +auths.auth_name=Ainm Fíordheimhnithe +auths.security_protocol=Prótacal Slándála auths.domain=Fearann +auths.host=Óstach +auths.port=Calafort +auths.bind_dn=Ceangail DN +auths.bind_password=Ceangail Pasfhocal +auths.user_base=Bonn Cuardaigh Úsáideora +auths.user_dn=Úsáideoir DN +auths.attribute_username=Tréith Ainm Úsáideora +auths.attribute_username_placeholder=Fág folamh chun an t-ainm úsáideora a iontráiltear i Gitea a úsáid. +auths.attribute_name=Tréith Céad Ainm +auths.attribute_surname=Tréith Sloinne +auths.attribute_mail=Tréith ríomhphoist +auths.attribute_ssh_public_key=Tréith Eochair SSH Phoiblí +auths.attribute_avatar=Tréith Avatar +auths.attributes_in_bind=Faigh tréithe i gComhthéacs Bind DN +auths.allow_deactivate_all=Lig do thoradh cuardaigh folamh gach úsáideoir a dhíghníomhachtú +auths.use_paged_search=Úsáid Cuardach Leathanaigh +auths.search_page_size=Méid an Leathanaigh +auths.filter=Scagaire Úsáideora +auths.admin_filter=Scagaire Riaracháin +auths.restricted_filter=Scagaire Srianta +auths.restricted_filter_helper=Fág folamh chun aon úsáideoirí a shocrú mar theoranta. Úsáid réiltín ('*') chun gach úsáideoir nach meaitseálann Scagaire Riaracháin a shocrú mar theoranta. +auths.verify_group_membership=Fíoraigh ballraíocht ghrúpa i LDAP (fág an scagaire folamh le scipeáil) +auths.group_search_base=Bonn Cuardaigh Grúpa DN +auths.group_attribute_list_users=Tréith Grúpa ina bhfuil Liosta Úsáideoirí +auths.user_attribute_in_group=Tréith Úsáideora atá Liostaithe i nGrúpa +auths.map_group_to_team=Léarscáil grúpaí LDAP chuig foirne na hEagraíochta (fág an réimse folamh le scipeáil) +auths.map_group_to_team_removal=Bain úsáideoirí ó fhoirne sioncronaithe mura mbaineann an t-úsáideoir leis an ngrúpa comhfhreagrach LDAP +auths.enable_ldap_groups=Cumasaigh grúpaí LDAP +auths.ms_ad_sa=MS AD Tréithe Cuardaigh +auths.smtp_auth=Cineál Fíordheimhnithe SMTP +auths.smtphost=Óstach SMTP +auths.smtpport=SMTP Calafort +auths.allowed_domains=Fearainn Ceadaithe +auths.allowed_domains_helper=Fág folamh chun gach fearann a cheadú. Déan ilfhearann a scaradh le camóg (','). +auths.skip_tls_verify=Scipeáil Fíorú TLS +auths.force_smtps=Fórsa SMTPS +auths.force_smtps_helper=Úsáidtear SMTPS i gcónaí ar chalafort 465. Socraigh é seo chun SMTPS a chur i bhfeidhm ar chalafoirt eile. (Seachas sin úsáidfear STARTTLS ar chalafoirt eile má thacaíonn an t-óstach leis.) +auths.helo_hostname=Ainm Óstach HELO +auths.helo_hostname_helper=Ainm óstach a sheoltar le HELO. Fág bán chun an t-ainm óstach reatha a sheoladh. +auths.disable_helo=Díchumasaigh HELO +auths.pam_service_name=Ainm Seirbhíse PAM +auths.pam_email_domain=Fearann Ríomhphoist PAM (roghnach) +auths.oauth2_provider=Soláthraí OAuth2 +auths.oauth2_icon_url=URL deilbhín +auths.oauth2_clientID=Aitheantas Cliant (Eochair) +auths.oauth2_clientSecret=Rúnda Cliant +auths.openIdConnectAutoDiscoveryURL=URL Fionnachtana Uathoibríoch OpenID Connect +auths.oauth2_use_custom_url=Úsáid URLanna Saincheaptha in ionad URLanna Réamhshocraithe +auths.oauth2_tokenURL=URL Comhartha +auths.oauth2_authURL=Údaraigh URL +auths.oauth2_profileURL=URL Próifíl +auths.oauth2_emailURL=URL ríomhphoist +auths.skip_local_two_fa=Scipeáil 2FA áitiúil +auths.skip_local_two_fa_helper=Ciallaíonn fágáil gan socrú go mbeidh ar úsáideoirí áitiúla a bhfuil tacar 2FA acu 2FA a rith fós chun logáil isteach +auths.oauth2_tenant=Tionónta +auths.oauth2_scopes=Scóipeanna Breise +auths.oauth2_required_claim_name=Ainm Éilimh Riachtanach +auths.oauth2_required_claim_name_helper=Socraigh an t-ainm seo chun logáil isteach ón bhfoinse seo a shrianadh d'úsáideoirí a bhfuil éileamh acu leis an ainm seo +auths.oauth2_required_claim_value=Luach Éilimh Riachtanach +auths.oauth2_required_claim_value_helper=Socraigh an luach seo chun logáil isteach ón bhfoinse seo a shrianadh chuig úsáideoirí a bhfuil éileamh acu leis an ainm agus an luach seo +auths.oauth2_group_claim_name=Ainm éileamh ag soláthar ainmneacha grúpa don fhoinse seo (Roghnach) +auths.oauth2_admin_group=Luach Éilimh Grúpa d'úsáideoirí riarthóra. (Roghnach - teastaíonn ainm éilimh thuas) +auths.oauth2_restricted_group=Luach Éilimh Grúpa d'úsáideoirí srianta. (Roghnach - teastaíonn ainm éilimh thuas) +auths.oauth2_map_group_to_team=Map mhaígh grúpaí chuig foirne Eagraíochta. (Roghnach - éilíonn ainm an éilimh thuas) +auths.oauth2_map_group_to_team_removal=Bain úsáideoirí ó fhoirne sioncronaithe mura mbaineann an t-úsáideoir leis an ngrúpa comhfhreagrach. +auths.enable_auto_register=Cumasaigh Clárú Auto +auths.sspi_auto_create_users=Cruthaigh úsáideoirí go huathoibríoch +auths.sspi_auto_create_users_helper=Lig do mhodh auth SSPI cuntais nua a chruthú go huathoibríoch d'úsáideoirí a logálann isteach den chéad uair +auths.sspi_auto_activate_users=Gníomhachtaigh úsáideoirí go huathoibríoch +auths.sspi_auto_activate_users_helper=Lig modh auth SSPI úsáideoirí nua a ghníomhachtú go huathoibríoch +auths.sspi_strip_domain_names=Bain ainmneacha fearann ó ainm úsáideora +auths.sspi_strip_domain_names_helper=Má dhéantar iad a sheiceáil, bainfear ainmneacha fearainn ó ainmneacha logála isteach (m.sh. Beidh “DOMAIN\ user” agus "user@example.org" araon ní bheidh ach “úsáideoir”). +auths.sspi_separator_replacement=Deighilteoir le húsáid in ionad\,/agus @ +auths.sspi_separator_replacement_helper=An carachtar a úsáidfear chun na deighilteoirí a chur in ionad na n-ainmneacha logála síos-leibhéil (m.sh. an \ i "DOMAIN\úsáideoir") agus ainmneacha príomhoidí úsáideora (m.sh. an @ in "user@example.org"). +auths.sspi_default_language=Teanga úsáideora réamhshocraithe +auths.sspi_default_language_helper=Teanga réamhshocraithe d'úsáideoirí cruthaithe go huathoibríoch ag modh auth SSPI. Fág folamh más fearr leat teanga a bhrath go huathoibríoch. +auths.tips=Leideanna +auths.tips.oauth2.general=OAuth2 Fíordheimhniú +auths.tips.oauth2.general.tip=Agus fíordheimhniú OAuth2 nua á chlárú agat, ba chóir go mbeadh an URL glaonna ais/atreoraithe: +auths.tip.oauth2_provider=Soláthraí OAuth2 +auths.tip.bitbucket=Cláraigh tomhaltóir OAuth nua ar %s agus cuir an cead 'Cuntas' - 'Léigh' leis +auths.tip.nextcloud=`Cláraigh tomhaltóir OAuth nua ar do chás ag baint úsáide as an roghchlár seo a leanas "Socruithe -> Slándáil -> cliant OAuth 2.0"` +auths.tip.dropbox=Cruthaigh feidhmchlár nua ag %s +auths.tip.facebook=Cláraigh feidhmchlár nua ag %s agus cuir an táirge "Facebook Login" leis +auths.tip.github=Cláraigh feidhmchlár OAuth nua ar %s +auths.tip.gitlab_new=Cláraigh feidhmchlár nua ar %s +auths.tip.google_plus=Faigh dintiúir chliaint OAuth2 ó chonsól API Google ag %s +auths.tip.openid_connect=Úsáid URL Fionnachtana OpenID Connect "https://{server}/.well-known/openid-configuration" chun na críochphointí a shonrú +auths.tip.twitter=Téigh go %s, cruthaigh feidhmchlár agus cinntigh go bhfuil an rogha "Ceadaigh úsáid a bhaint as an bhfeidhmchlár seo chun logáil isteach le Twitter" cumasaithe +auths.tip.discord=Cláraigh feidhmchlár nua ar %s +auths.tip.gitea=Cláraigh feidhmchlár OAuth2 nua. Tá treoir le fáil ag %s +auths.tip.yandex=`Cruthaigh feidhmchlár nua ag %s. Roghnaigh na ceadanna seo a leanas ón rannán "Yandex.Passport API": "Rochtain ar sheoladh ríomhphoist", "Rochtain ar avatar úsáideora" agus "Rochtain ar ainm úsáideora, céad ainm agus sloinne, inscne"` +auths.tip.mastodon=Ionchur URL sampla saincheaptha don shampla mastodon is mian leat a fhíordheimhniú leis (nó bain úsáid as an gceann réamhshocraithe) +auths.edit=Cuir Foinse Fíordheimhnithe in Eagar +auths.activated=Tá an Foinse Fíordheimhnithe seo gníomhachtaithe +auths.new_success=Tá an fíordheimhniú "%s" curtha leis. +auths.update_success=Nuashonraíodh an fhoinse fíordheimhnithe. +auths.update=Nuashonraigh Foinse Fíordheimhnithe +auths.delete=Scrios Foinse Fíordheimhnithe +auths.delete_auth_title=Scrios Foinse Fíordheimhnithe +auths.delete_auth_desc=Má scriosann tú foinse fíordheimhnithe cuirtear cosc ​​ar úsáideoirí í a úsáid chun síniú isteach. Lean ort? +auths.still_in_used=Tá an fhoinse fíordheimhnithe fós in úsáid. Tiontaigh nó scrios aon úsáideoir a úsáideann an fhoinse fíordheimhnithe seo ar dtús. +auths.deletion_success=Tá an fhoinse fíordheimhnithe scriosta. +auths.login_source_exist=Tá an fhoinse fíordheimhnithe "%s" ann cheana. +auths.login_source_of_type_exist=Tá foinse fíordheimhnithe den chineál seo ann cheana féin. +auths.unable_to_initialize_openid=Ní féidir Soláthraí Ceangail OpenID a thionscnamh: %s +auths.invalid_openIdConnectAutoDiscoveryURL=URL Neamhbhailí Fionnachtana Uathoibríoch (ní mór gur URL bailí é seo ag tosú le http:// nó https://) +config.server_config=Cumraíocht Freastalaí +config.app_name=Teideal an Láithreáin +config.app_ver=Leagan Gitea +config.app_url=URL Bonn Gitea +config.custom_conf=Cosán Comhad Cumraíochta +config.custom_file_root_path=Cosán Fréamh Comhad Saincheaptha +config.domain=Fearann ​​Freastalaí +config.offline_mode=Mód Áitiúil +config.disable_router_log=Díchumasaigh Loga an Ródaire +config.run_user=Rith Mar Ainm úsáideora +config.run_mode=Mód Rith +config.git_version=Leagan Git +config.app_data_path=Cosán Sonraí Aip +config.repo_root_path=Cosán Fréimhe Stórála +config.lfs_root_path=Cosán Fréamh LFS +config.log_file_root_path=Cosán Logála +config.script_type=Cineál Script +config.reverse_auth_user=Úsáideoir Fíordheimhnithe Droim ar Ais +config.ssh_config=Cumraíocht SSH +config.ssh_enabled=Cumasaithe +config.ssh_start_builtin_server=Úsáid Freastalaí Ionsuite +config.ssh_domain=Fearainn Freastalaí SSH +config.ssh_port=Calafort +config.ssh_listen_port=Éist Calafort +config.ssh_root_path=Cosán Fréimhe +config.ssh_key_test_path=Cosán Tástáil Eochair +config.ssh_keygen_path=Keygen ('ssh-keygen') Cosán +config.ssh_minimum_key_size_check=Seiceáil Íosta Méid Eochair +config.ssh_minimum_key_sizes=Méideanna Íosta Eochrach +config.lfs_config=Cumraíocht LFS +config.lfs_enabled=Cumasaithe +config.lfs_content_path=Cosán Ábhar LFS +config.lfs_http_auth_expiry=Éag Auth LFS HTTP +config.db_config=Cumraíocht Bunachar Sonraí +config.db_type=Cineál +config.db_host=Óstach +config.db_name=Ainm +config.db_user=Ainm úsáideora +config.db_schema=Scéim +config.db_ssl_mode=SSL +config.db_path=Cosán +config.service_config=Cumraíocht Seirbhíse +config.register_email_confirm=Deimhniú Ríomhphost a éileamh chun Clárú +config.disable_register=Díchumasaigh Féin-Chlárú +config.allow_only_internal_registration=Ceadaigh Clárú Amháin Trí Gitea féin +config.allow_only_external_registration=Ceadaigh Clárú Trí Sheirbhísí Seachtracha amháin +config.enable_openid_signup=Cumasaigh Féinchlárú OpenID +config.enable_openid_signin=Cumasaigh Síniú isteach OpenID +config.show_registration_button=Taispeáin Cnaipe Cláraithe +config.require_sign_in_view=Teastaíonn Sínigh isteach chun Leathanaigh Amharc +config.mail_notify=Cumasaigh Fógraí Ríomhphoist +config.enable_captcha=Cumasaigh CAPTCHA +config.active_code_lives=Saol Gníomhach ag an gCód +config.reset_password_code_lives=Am Éaga Chóid Aisghabhála Cuntais +config.default_keep_email_private=Folaigh Seoltaí Ríomhphoist de réir Réamhshocrú +config.default_allow_create_organization=Ceadaigh Cruthú Eagraíochtaí de réir Réamhshocrú +config.enable_timetracking=Cumasaigh Rianú Ama +config.default_enable_timetracking=Cumasaigh Rianú Ama de réir Réamhshocrú +config.default_allow_only_contributors_to_track_time=Lig do Rannpháirtithe Amháin Rianú Am +config.no_reply_address=Fearann Ríomhphoist Folaithe +config.default_visibility_organization=Infheictheacht réamhshocraithe d'Eagraíochtaí nua +config.default_enable_dependencies=Cumasaigh Spleáchais Eisithe de réir Réamhshocrú config.webhook_config=Cumraíocht Crúca Gréasán +config.queue_length=Fad scuaine +config.deliver_timeout=Teorainn Ama Seachadta +config.skip_tls_verify=Scipeáil Fíorú TLS +config.mailer_config=Cumraíocht Seoltóra +config.mailer_enabled=Cumasaithe +config.mailer_enable_helo=Cumasaigh HELO +config.mailer_name=Ainm +config.mailer_protocol=Prótacal +config.mailer_smtp_addr=Seoladh SMTP +config.mailer_smtp_port=Calafort SMTP +config.mailer_user=Úsáideoir +config.mailer_use_sendmail=Úsáid Sendmail +config.mailer_sendmail_path=Cosán Sendmail +config.mailer_sendmail_args=Argóintí Breise chuig Sendmail +config.mailer_sendmail_timeout=Teorainn Ama Sendmail +config.mailer_use_dummy=Caochadán +config.test_email_placeholder=Ríomhphost (m.sh. test@example.com) +config.send_test_mail=Seol Ríomhphost Tástála +config.send_test_mail_submit=Seol +config.test_mail_failed=Theip ar ríomhphost tástála a sheoladh chuig "%s": %v +config.test_mail_sent=Tá ríomhphost tástála seolta chuig "%s". +config.oauth_config=Cumraíocht OAuth +config.oauth_enabled=Cumasaithe +config.cache_config=Cumraíocht taisce +config.cache_adapter=Cuibheoir taisce +config.cache_interval=Eatramh Taisce +config.cache_conn=Ceangal Taisce +config.cache_item_ttl=Mír Taisce TTL +config.cache_test=Taisce Tástáil +config.cache_test_failed=Theip ar an taisce a thaiscéaladh: %v. +config.cache_test_slow=D'éirigh leis an tástáil taisce, ach tá an freagra mall: %s. +config.cache_test_succeeded=D'éirigh leis an tástáil taisce, fuair sé freagra i %s. +config.session_config=Cumraíocht Seisiúin +config.session_provider=Soláthraí Seisiúin +config.provider_config=Cumraíocht Soláthraí +config.cookie_name=Ainm Fianán +config.gc_interval_time=Am Eatramh GC +config.session_life_time=Am Saoil na Seisiúin +config.https_only=HTTPS Amháin +config.cookie_life_time=Am Saoil Fianán +config.picture_config=Cumraíocht Pictiúr agus Avatar +config.picture_service=Seirbhís Pictiúr +config.disable_gravatar=Díchumasaigh Gravatar +config.enable_federated_avatar=Cumasaigh Avatars Cónaidhme +config.open_with_editor_app_help=Na heagarthóirí "Oscailte le" don roghchlár Clón. Má fhágtar folamh é, úsáidfear an réamhshocrú. Leathnaigh chun an réamhshocrú a fheiceáil. +config.git_config=Cumraíocht Git +config.git_disable_diff_highlight=Díchumasaigh Aibhsiú Comhréire Diff +config.git_max_diff_lines=Max Diff Lines (do chomhad amháin) +config.git_max_diff_line_characters=Carachtair Max Diff (le haghaidh líne amháin) +config.git_max_diff_files=Comhaid Max Diff (le taispeáint) +config.git_gc_args=Argóintí GC +config.git_migrate_timeout=Teorainn Ama Imirce +config.git_mirror_timeout=Teorainn Ama Nuashonraithe Scátháin +config.git_clone_timeout=Teorainn Ama Oibríochta Clón +config.git_pull_timeout=Tarraing Am Oibríochta +config.git_gc_timeout=Teorainn Ama Oibriúcháin GC +config.log_config=Cumraíocht Logáil +config.logger_name_fmt=Logálaí: %s +config.disabled_logger=Díchumasaithe +config.access_log_mode=Mód Logáil Rochtana +config.access_log_template=Teimpléad Logáil Rochtana +config.xorm_log_sql=Logáil SQL +config.set_setting_failed=Theip ar shocrú %s a shocrú +monitor.stats=Staitisticí +monitor.cron=Tascanna Cron +monitor.name=Ainm +monitor.schedule=Sceideal +monitor.next=An chéad uair eile +monitor.previous=Am Roimhe Seo +monitor.execute_times=Forghníomhaíochtaí +monitor.process=Próisis reatha +monitor.stacktrace=Rian cruachta +monitor.processes_count=Próisis %d +monitor.download_diagnosis_report=Íoslódáil tuairisc diagnóis monitor.desc=Cur síos +monitor.start=Am Tosaigh +monitor.execute_time=Am Forghníomhaithe +monitor.last_execution_result=Toradh +monitor.process.cancel=Cealaigh próiseas +monitor.process.cancel_desc=Má chuirtear próiseas ar ceal d'fhéadfadh go gcaillfí sonraí +monitor.process.cancel_notices=Cealaigh: %s? +monitor.process.children=Leanaí +monitor.queues=Scuaineanna +monitor.queue=Scuaine: %s +monitor.queue.name=Ainm +monitor.queue.type=Cineál +monitor.queue.exemplar=Cineál Eiseamláire +monitor.queue.numberworkers=Líon na nOibrithe +monitor.queue.activeworkers=Oibrithe Gníomhacha +monitor.queue.maxnumberworkers=Líon Uasta na nOibrithe +monitor.queue.numberinqueue=Uimhir i scuaine +monitor.queue.review_add=Athbhreithniú / Cuir Oibrithe leis +monitor.queue.settings.title=Socruithe Linn +monitor.queue.settings.desc=Fásann linnte go dinimiciúil mar fhreagra ar a gcuid scuaine oibrithe a bhlocáil. +monitor.queue.settings.maxnumberworkers=Uaslíon na n-oibrithe +monitor.queue.settings.maxnumberworkers.placeholder=Faoi láthair %[1]d +monitor.queue.settings.maxnumberworkers.error=Caithfidh uaslíon na n-oibrithe a bheith ina uimhir monitor.queue.settings.submit=Nuashonrú Socruithe +monitor.queue.settings.changed=Socruithe Nuashonraithe +monitor.queue.settings.remove_all_items=Bain gach +monitor.queue.settings.remove_all_items_done=Baineadh na míreanna go léir sa scuaine. notices.system_notice_list=Fógraí Córais +notices.view_detail_header=Féach ar Sonraí Fógra notices.operations=Oibríochtaí +notices.select_all=Roghnaigh Gach +notices.deselect_all=Díroghnaigh Gach +notices.inverse_selection=Roghnú Inbhéartha +notices.delete_selected=Scrios Roghnaithe +notices.delete_all=Scrios Gach Fógra +notices.type=Cineál +notices.type_1=Stóras +notices.type_2=Tasc notices.desc=Cur síos +notices.op=Oibríocht. +notices.delete_success=Scriosadh na fógraí córais. [action] [tool] +now=anois +future=todhchaí +1s=1 soicind +1m=1 nóiméad +1h=1 uair an chloig +1d=1 lá +1w=1 seachtain +1mon=1 mhí +1y=1 bhliain +seconds=%d soicind +minutes=%d nóiméad [dropzone] [notification] +notifications=Fógraí +unread=Gan léamh +read=Léigh +subscriptions=Síntiúis +watching=Ag féachaint +no_subscriptions=Gan síntiúis [gpg] [units] +unit=Aonad [packages] +title=Pacáistí +filter.type=Cineál +filter.type.all=Gach +filter.container.tagged=Clibeáilte +filter.container.untagged=Gan chlib +details=Sonraí +details.author=Údar +dependency.version=Leagan alpine.repository.branches=Brainsí alpine.repository.repositories=Stórais +conan.details.repository=Stóras +container.details.type=Cineál Íomhá +container.details.platform=Ardán +container.multi_arch=Córas Oibriúcháin / Ailtireacht +container.labels=Lipéid +container.labels.key=Eochair +container.labels.value=Luach +debian.repository=Eolas Stóras +debian.repository.components=Comhpháirteanna +debian.repository.architectures=Ailtireachtaí +npm.details.tag=Clib +owner.settings.cleanuprules.enabled=Cumasaithe [secrets] @@ -2962,10 +3438,15 @@ alpine.repository.repositories=Stórais +runners.name=Ainm +runners.owner_type=Cineál runners.description=Cur síos +runners.labels=Lipéid runners.task_list.run=Rith +runners.task_list.repository=Stóras runners.task_list.commit=Tiomantas runners.status.active=Gníomhach +runners.version=Leagan runners.reset_registration_token=Athshocraigh comhartha clár runners.reset_registration_token_success=D'éirigh le hathshocrú comhartha clárúcháin an dara háit diff --git a/routers/api/packages/api.go b/routers/api/packages/api.go index 0f42e8f59e..d17e4875b1 100644 --- a/routers/api/packages/api.go +++ b/routers/api/packages/api.go @@ -63,6 +63,20 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.Context) { ctx.Error(http.StatusUnauthorized, "reqPackageAccess", "user should have specific permission or be a site admin") return } + + // check if scope only applies to public resources + publicOnly, err := scope.PublicOnly() + if err != nil { + ctx.Error(http.StatusForbidden, "tokenRequiresScope", "parsing public resource scope failed: "+err.Error()) + return + } + + if publicOnly { + if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() { + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public packages") + return + } + } } } diff --git a/routers/api/v1/api.go b/routers/api/v1/api.go index 5aa8ad44e5..883e694e44 100644 --- a/routers/api/v1/api.go +++ b/routers/api/v1/api.go @@ -235,6 +235,62 @@ func reqPackageAccess(accessMode perm.AccessMode) func(ctx *context.APIContext) } } +func checkTokenPublicOnly() func(ctx *context.APIContext) { + return func(ctx *context.APIContext) { + if !ctx.PublicOnly { + return + } + + requiredScopeCategories, ok := ctx.Data["requiredScopeCategories"].([]auth_model.AccessTokenScopeCategory) + if !ok || len(requiredScopeCategories) == 0 { + return + } + + // public Only permission check + switch { + case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryRepository): + if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate { + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public repos") + return + } + case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryIssue): + if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate { + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public issues") + return + } + case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryOrganization): + if ctx.Org.Organization != nil && ctx.Org.Organization.Visibility != api.VisibleTypePublic { + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public orgs") + return + } + if ctx.ContextUser != nil && ctx.ContextUser.IsOrganization() && ctx.ContextUser.Visibility != api.VisibleTypePublic { + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public orgs") + return + } + case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryUser): + if ctx.ContextUser != nil && ctx.ContextUser.IsUser() && ctx.ContextUser.Visibility != api.VisibleTypePublic { + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public users") + return + } + case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryActivityPub): + if ctx.ContextUser != nil && ctx.ContextUser.IsUser() && ctx.ContextUser.Visibility != api.VisibleTypePublic { + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public activitypub") + return + } + case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryNotification): + if ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate { + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public notifications") + return + } + case auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryPackage): + if ctx.Package != nil && ctx.Package.Owner.Visibility.IsPrivate() { + ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public packages") + return + } + } + } +} + // if a token is being used for auth, we check that it contains the required scope // if a token is not being used, reqToken will enforce other sign in methods func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeCategory) func(ctx *context.APIContext) { @@ -250,9 +306,6 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC return } - ctx.Data["ApiTokenScopePublicRepoOnly"] = false - ctx.Data["ApiTokenScopePublicOrgOnly"] = false - // use the http method to determine the access level requiredScopeLevel := auth_model.Read if ctx.Req.Method == "POST" || ctx.Req.Method == "PUT" || ctx.Req.Method == "PATCH" || ctx.Req.Method == "DELETE" { @@ -261,6 +314,18 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC // get the required scope for the given access level and category requiredScopes := auth_model.GetRequiredScopes(requiredScopeLevel, requiredScopeCategories...) + allow, err := scope.HasScope(requiredScopes...) + if err != nil { + ctx.Error(http.StatusForbidden, "tokenRequiresScope", "checking scope failed: "+err.Error()) + return + } + + if !allow { + ctx.Error(http.StatusForbidden, "tokenRequiresScope", fmt.Sprintf("token does not have at least one of required scope(s): %v", requiredScopes)) + return + } + + ctx.Data["requiredScopeCategories"] = requiredScopeCategories // check if scope only applies to public resources publicOnly, err := scope.PublicOnly() @@ -269,21 +334,8 @@ func tokenRequiresScopes(requiredScopeCategories ...auth_model.AccessTokenScopeC return } - // this context is used by the middleware in the specific route - ctx.Data["ApiTokenScopePublicRepoOnly"] = publicOnly && auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryRepository) - ctx.Data["ApiTokenScopePublicOrgOnly"] = publicOnly && auth_model.ContainsCategory(requiredScopeCategories, auth_model.AccessTokenScopeCategoryOrganization) - - allow, err := scope.HasScope(requiredScopes...) - if err != nil { - ctx.Error(http.StatusForbidden, "tokenRequiresScope", "checking scope failed: "+err.Error()) - return - } - - if allow { - return - } - - ctx.Error(http.StatusForbidden, "tokenRequiresScope", fmt.Sprintf("token does not have at least one of required scope(s): %v", requiredScopes)) + // assign to true so that those searching should only filter public repositories/users/organizations + ctx.PublicOnly = publicOnly } } @@ -295,25 +347,6 @@ func reqToken() func(ctx *context.APIContext) { return } - if true == ctx.Data["IsApiToken"] { - publicRepo, pubRepoExists := ctx.Data["ApiTokenScopePublicRepoOnly"] - publicOrg, pubOrgExists := ctx.Data["ApiTokenScopePublicOrgOnly"] - - if pubRepoExists && publicRepo.(bool) && - ctx.Repo.Repository != nil && ctx.Repo.Repository.IsPrivate { - ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public repos") - return - } - - if pubOrgExists && publicOrg.(bool) && - ctx.Org.Organization != nil && ctx.Org.Organization.Visibility != api.VisibleTypePublic { - ctx.Error(http.StatusForbidden, "reqToken", "token scope is limited to public orgs") - return - } - - return - } - if ctx.IsSigned { return } @@ -879,11 +912,11 @@ func Routes() *web.Router { m.Group("/user/{username}", func() { m.Get("", activitypub.Person) m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) - }, context.UserAssignmentAPI()) + }, context.UserAssignmentAPI(), checkTokenPublicOnly()) m.Group("/user-id/{user-id}", func() { m.Get("", activitypub.Person) m.Post("/inbox", activitypub.ReqHTTPSignature(), activitypub.PersonInbox) - }, context.UserIDAssignmentAPI()) + }, context.UserIDAssignmentAPI(), checkTokenPublicOnly()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryActivityPub)) } @@ -939,7 +972,7 @@ func Routes() *web.Router { }, reqSelfOrAdmin(), reqBasicOrRevProxyAuth()) m.Get("/activities/feeds", user.ListUserActivityFeeds) - }, context.UserAssignmentAPI(), individualPermsChecker) + }, context.UserAssignmentAPI(), checkTokenPublicOnly(), individualPermsChecker) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser)) // Users (requires user scope) @@ -957,7 +990,7 @@ func Routes() *web.Router { m.Get("/starred", user.GetStarredRepos) m.Get("/subscriptions", user.GetWatchedRepos) - }, context.UserAssignmentAPI()) + }, context.UserAssignmentAPI(), checkTokenPublicOnly()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) // Users (requires user scope) @@ -1044,7 +1077,7 @@ func Routes() *web.Router { m.Get("", user.IsStarring) m.Put("", user.Star) m.Delete("", user.Unstar) - }, repoAssignment()) + }, repoAssignment(), checkTokenPublicOnly()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) m.Get("/times", repo.ListMyTrackedTimes) m.Get("/stopwatches", repo.GetStopwatches) @@ -1069,18 +1102,20 @@ func Routes() *web.Router { m.Get("", user.CheckUserBlock) m.Put("", user.BlockUser) m.Delete("", user.UnblockUser) - }, context.UserAssignmentAPI()) + }, context.UserAssignmentAPI(), checkTokenPublicOnly()) }) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser), reqToken()) // Repositories (requires repo scope, org scope) m.Post("/org/{org}/repos", + // FIXME: we need org in context tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization, auth_model.AccessTokenScopeCategoryRepository), reqToken(), bind(api.CreateRepoOption{}), repo.CreateOrgRepoDeprecated) // requires repo scope + // FIXME: Don't expose repository id outside of the system m.Combo("/repositories/{id}", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)).Get(repo.GetByID) // Repos (requires repo scope) @@ -1334,7 +1369,7 @@ func Routes() *web.Router { m.Post("", bind(api.UpdateRepoAvatarOption{}), repo.UpdateAvatar) m.Delete("", repo.DeleteAvatar) }, reqAdmin(), reqToken()) - }, repoAssignment()) + }, repoAssignment(), checkTokenPublicOnly()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryRepository)) // Notifications (requires notifications scope) @@ -1343,7 +1378,7 @@ func Routes() *web.Router { m.Combo("/notifications", reqToken()). Get(notify.ListRepoNotifications). Put(notify.ReadRepoNotifications) - }, repoAssignment()) + }, repoAssignment(), checkTokenPublicOnly()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryNotification)) // Issue (requires issue scope) @@ -1457,7 +1492,7 @@ func Routes() *web.Router { Patch(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), bind(api.EditMilestoneOption{}), repo.EditMilestone). Delete(reqToken(), reqRepoWriter(unit.TypeIssues, unit.TypePullRequests), repo.DeleteMilestone) }) - }, repoAssignment()) + }, repoAssignment(), checkTokenPublicOnly()) }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryIssue)) // NOTE: these are Gitea package management API - see packages.CommonRoutes and packages.DockerContainerRoutes for endpoints that implement package manager APIs @@ -1468,14 +1503,14 @@ func Routes() *web.Router { m.Get("/files", reqToken(), packages.ListPackageFiles) }) m.Get("/", reqToken(), packages.ListPackages) - }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead)) + }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryPackage), context.UserAssignmentAPI(), context.PackageAssignmentAPI(), reqPackageAccess(perm.AccessModeRead), checkTokenPublicOnly()) // Organizations m.Get("/user/orgs", reqToken(), tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), org.ListMyOrgs) m.Group("/users/{username}/orgs", func() { m.Get("", reqToken(), org.ListUserOrgs) m.Get("/{org}/permissions", reqToken(), org.GetUserOrgsPermissions) - }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context.UserAssignmentAPI()) + }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryUser, auth_model.AccessTokenScopeCategoryOrganization), context.UserAssignmentAPI(), checkTokenPublicOnly()) m.Post("/orgs", tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), reqToken(), bind(api.CreateOrgOption{}), org.Create) m.Get("/orgs", org.GetAll, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization)) m.Group("/orgs/{org}", func() { @@ -1533,7 +1568,7 @@ func Routes() *web.Router { m.Delete("", org.UnblockUser) }) }, reqToken(), reqOrgOwnership()) - }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true)) + }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(true), checkTokenPublicOnly()) m.Group("/teams/{teamid}", func() { m.Combo("").Get(reqToken(), org.GetTeam). Patch(reqToken(), reqOrgOwnership(), bind(api.EditTeamOption{}), org.EditTeam). @@ -1553,7 +1588,7 @@ func Routes() *web.Router { Get(reqToken(), org.GetTeamRepo) }) m.Get("/activities/feeds", org.ListTeamActivityFeeds) - }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(false, true), reqToken(), reqTeamMembership()) + }, tokenRequiresScopes(auth_model.AccessTokenScopeCategoryOrganization), orgAssignment(false, true), reqToken(), reqTeamMembership(), checkTokenPublicOnly()) m.Group("/admin", func() { m.Group("/cron", func() { diff --git a/routers/api/v1/org/org.go b/routers/api/v1/org/org.go index e848d95181..9e58746272 100644 --- a/routers/api/v1/org/org.go +++ b/routers/api/v1/org/org.go @@ -191,7 +191,7 @@ func GetAll(ctx *context.APIContext) { // "$ref": "#/responses/OrganizationList" vMode := []api.VisibleType{api.VisibleTypePublic} - if ctx.IsSigned { + if ctx.IsSigned && !ctx.PublicOnly { vMode = append(vMode, api.VisibleTypeLimited) if ctx.Doer.IsAdmin { vMode = append(vMode, api.VisibleTypePrivate) diff --git a/routers/api/v1/repo/issue.go b/routers/api/v1/repo/issue.go index c1218440e5..d8c39b0f69 100644 --- a/routers/api/v1/repo/issue.go +++ b/routers/api/v1/repo/issue.go @@ -149,7 +149,7 @@ func SearchIssues(ctx *context.APIContext) { Actor: ctx.Doer, } if ctx.IsSigned { - opts.Private = true + opts.Private = !ctx.PublicOnly opts.AllLimited = true } if ctx.FormString("owner") != "" { diff --git a/routers/api/v1/repo/repo.go b/routers/api/v1/repo/repo.go index 6c1a94ee16..4638e2ba5c 100644 --- a/routers/api/v1/repo/repo.go +++ b/routers/api/v1/repo/repo.go @@ -129,6 +129,11 @@ func Search(ctx *context.APIContext) { // "422": // "$ref": "#/responses/validationError" + private := ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private")) + if ctx.PublicOnly { + private = false + } + opts := &repo_model.SearchRepoOptions{ ListOptions: utils.GetListOptions(ctx), Actor: ctx.Doer, @@ -138,7 +143,7 @@ func Search(ctx *context.APIContext) { TeamID: ctx.FormInt64("team_id"), TopicOnly: ctx.FormBool("topic"), Collaborate: optional.None[bool](), - Private: ctx.IsSigned && (ctx.FormString("private") == "" || ctx.FormBool("private")), + Private: private, Template: optional.None[bool](), StarredByID: ctx.FormInt64("starredBy"), IncludeDescription: ctx.FormBool("includeDesc"), diff --git a/routers/api/v1/user/user.go b/routers/api/v1/user/user.go index 2c277a18c7..a9011427fb 100644 --- a/routers/api/v1/user/user.go +++ b/routers/api/v1/user/user.go @@ -9,6 +9,7 @@ import ( activities_model "code.gitea.io/gitea/models/activities" user_model "code.gitea.io/gitea/models/user" + "code.gitea.io/gitea/modules/structs" "code.gitea.io/gitea/routers/api/v1/utils" "code.gitea.io/gitea/services/context" "code.gitea.io/gitea/services/convert" @@ -67,12 +68,17 @@ func Search(ctx *context.APIContext) { maxResults = 1 users = []*user_model.User{user_model.NewActionsUser()} default: + var visible []structs.VisibleType + if ctx.PublicOnly { + visible = []structs.VisibleType{structs.VisibleTypePublic} + } users, maxResults, err = user_model.SearchUsers(ctx, &user_model.SearchUserOptions{ Actor: ctx.Doer, Keyword: ctx.FormTrim("q"), UID: uid, Type: user_model.UserTypeIndividual, SearchByEmail: true, + Visible: visible, ListOptions: listOptions, }) if err != nil { diff --git a/services/context/api.go b/services/context/api.go index 84da526e74..00cfd6afd9 100644 --- a/services/context/api.go +++ b/services/context/api.go @@ -35,9 +35,10 @@ type APIContext struct { ContextUser *user_model.User // the user which is being visited, in most cases it differs from Doer - Repo *Repository - Org *APIOrganization - Package *Package + Repo *Repository + Org *APIOrganization + Package *Package + PublicOnly bool // Whether the request is for a public endpoint } func init() { diff --git a/templates/repo/empty.tmpl b/templates/repo/empty.tmpl index cb2a5ba1e9..7613643351 100644 --- a/templates/repo/empty.tmpl +++ b/templates/repo/empty.tmpl @@ -24,7 +24,7 @@
-

{{ctx.Locale.Tr "repo.clone_this_repo"}} {{ctx.Locale.Tr "repo.clone_helper" "http://git-scm.com/book/en/Git-Basics-Getting-a-Git-Repository"}}

+

{{ctx.Locale.Tr "repo.clone_this_repo"}} {{ctx.Locale.Tr "repo.clone_helper" "http://git-scm.com/book/en/v2/Git-Basics-Getting-a-Git-Repository"}}

{{if and .CanWriteCode (not .Repository.IsArchived)}} diff --git a/tests/integration/api_issue_test.go b/tests/integration/api_issue_test.go index 8bfb6fabe2..5b9f16ef96 100644 --- a/tests/integration/api_issue_test.go +++ b/tests/integration/api_issue_test.go @@ -75,6 +75,34 @@ func TestAPIListIssues(t *testing.T) { } } +func TestAPIListIssuesPublicOnly(t *testing.T) { + defer tests.PrepareTestEnv(t)() + + repo1 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 1}) + owner1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo1.OwnerID}) + + session := loginUser(t, owner1.Name) + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue) + link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner1.Name, repo1.Name)) + link.RawQuery = url.Values{"state": {"all"}}.Encode() + req := NewRequest(t, "GET", link.String()).AddTokenAuth(token) + MakeRequest(t, req, http.StatusOK) + + repo2 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 2}) + owner2 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: repo2.OwnerID}) + + session = loginUser(t, owner2.Name) + token = getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue) + link, _ = url.Parse(fmt.Sprintf("/api/v1/repos/%s/%s/issues", owner2.Name, repo2.Name)) + link.RawQuery = url.Values{"state": {"all"}}.Encode() + req = NewRequest(t, "GET", link.String()).AddTokenAuth(token) + MakeRequest(t, req, http.StatusOK) + + publicOnlyToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopePublicOnly) + req = NewRequest(t, "GET", link.String()).AddTokenAuth(publicOnlyToken) + MakeRequest(t, req, http.StatusForbidden) +} + func TestAPICreateIssue(t *testing.T) { defer tests.PrepareTestEnv(t)() const body, title = "apiTestBody", "apiTestTitle" @@ -243,6 +271,12 @@ func TestAPISearchIssues(t *testing.T) { DecodeJSON(t, resp, &apiIssues) assert.Len(t, apiIssues, expectedIssueCount) + publicOnlyToken := getUserToken(t, "user1", auth_model.AccessTokenScopeReadIssue, auth_model.AccessTokenScopePublicOnly) + req = NewRequest(t, "GET", link.String()).AddTokenAuth(publicOnlyToken) + resp = MakeRequest(t, req, http.StatusOK) + DecodeJSON(t, resp, &apiIssues) + assert.Len(t, apiIssues, 15) // 15 public issues + since := "2000-01-01T00:50:01+00:00" // 946687801 before := time.Unix(999307200, 0).Format(time.RFC3339) query.Add("since", since) diff --git a/tests/integration/api_repo_branch_test.go b/tests/integration/api_repo_branch_test.go index b0ac2286c9..63080b308c 100644 --- a/tests/integration/api_repo_branch_test.go +++ b/tests/integration/api_repo_branch_test.go @@ -28,9 +28,13 @@ func TestAPIRepoBranchesPlain(t *testing.T) { repo3 := unittest.AssertExistsAndLoadBean(t, &repo_model.Repository{ID: 3}) user1 := unittest.AssertExistsAndLoadBean(t, &user_model.User{ID: 1}) session := loginUser(t, user1.LowerName) - token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) + // public only token should be forbidden + publicOnlyToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopePublicOnly, auth_model.AccessTokenScopeWriteRepository) link, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches", repo3.Name)) // a plain repo + MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden) + + token := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeWriteRepository) resp := MakeRequest(t, NewRequest(t, "GET", link.String()).AddTokenAuth(token), http.StatusOK) bs, err := io.ReadAll(resp.Body) assert.NoError(t, err) @@ -42,6 +46,8 @@ func TestAPIRepoBranchesPlain(t *testing.T) { assert.EqualValues(t, "master", branches[1].Name) link2, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches/test_branch", repo3.Name)) + MakeRequest(t, NewRequest(t, "GET", link2.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden) + resp = MakeRequest(t, NewRequest(t, "GET", link2.String()).AddTokenAuth(token), http.StatusOK) bs, err = io.ReadAll(resp.Body) assert.NoError(t, err) @@ -49,6 +55,8 @@ func TestAPIRepoBranchesPlain(t *testing.T) { assert.NoError(t, json.Unmarshal(bs, &branch)) assert.EqualValues(t, "test_branch", branch.Name) + MakeRequest(t, NewRequest(t, "POST", link.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden) + req := NewRequest(t, "POST", link.String()).AddTokenAuth(token) req.Header.Add("Content-Type", "application/json") req.Body = io.NopCloser(bytes.NewBufferString(`{"new_branch_name":"test_branch2", "old_branch_name": "test_branch", "old_ref_name":"refs/heads/test_branch"}`)) @@ -73,6 +81,7 @@ func TestAPIRepoBranchesPlain(t *testing.T) { link3, _ := url.Parse(fmt.Sprintf("/api/v1/repos/org3/%s/branches/test_branch2", repo3.Name)) MakeRequest(t, NewRequest(t, "DELETE", link3.String()), http.StatusNotFound) + MakeRequest(t, NewRequest(t, "DELETE", link3.String()).AddTokenAuth(publicOnlyToken), http.StatusForbidden) MakeRequest(t, NewRequest(t, "DELETE", link3.String()).AddTokenAuth(token), http.StatusNoContent) assert.NoError(t, err) diff --git a/tests/integration/api_user_search_test.go b/tests/integration/api_user_search_test.go index ff4671c54e..e9805a5139 100644 --- a/tests/integration/api_user_search_test.go +++ b/tests/integration/api_user_search_test.go @@ -38,6 +38,19 @@ func TestAPIUserSearchLoggedIn(t *testing.T) { assert.Contains(t, user.UserName, query) assert.NotEmpty(t, user.Email) } + + publicToken := getTokenForLoggedInUser(t, session, auth_model.AccessTokenScopeReadUser, auth_model.AccessTokenScopePublicOnly) + req = NewRequestf(t, "GET", "/api/v1/users/search?q=%s", query). + AddTokenAuth(publicToken) + resp = MakeRequest(t, req, http.StatusOK) + results = SearchResults{} + DecodeJSON(t, resp, &results) + assert.NotEmpty(t, results.Data) + for _, user := range results.Data { + assert.Contains(t, user.UserName, query) + assert.NotEmpty(t, user.Email) + assert.True(t, user.Visibility == "public") + } } func TestAPIUserSearchNotLoggedIn(t *testing.T) { diff --git a/web_src/js/features/repo-home.ts b/web_src/js/features/repo-home.ts index f3e39ddb3c..ed1415d286 100644 --- a/web_src/js/features/repo-home.ts +++ b/web_src/js/features/repo-home.ts @@ -60,7 +60,7 @@ export function initRepoTopicBar() { // how to test: input topic like " invalid topic " (with spaces), and select it from the list, then "Save" const responseData = await response.json(); lastErrorToast = showErrorToast(responseData.message, {duration: 5000}); - if (responseData.invalidTopics.length > 0) { + if (responseData.invalidTopics && responseData.invalidTopics.length > 0) { const {invalidTopics} = responseData; const topicLabels = queryElemChildren(topicDropdown, 'a.ui.label'); for (const [index, value] of topics.split(',').entries()) {