mirror of
				https://github.com/superseriousbusiness/gotosocial.git
				synced 2025-10-31 03:52:24 -05:00 
			
		
		
		
	Oidc (#109)
* add oidc config * inching forward with oidc idp * lil webfingy fix * bit more progress * further oidc * oidc now working * document dex config * replace broken images * add additional credits * tiny doc update * update * add oidc config * inching forward with oidc idp * bit more progress * further oidc * oidc now working * document dex config * replace broken images * add additional credits * tiny doc update * update * document * docs + comments
This commit is contained in:
		
					parent
					
						
							
								113186ce4e
							
						
					
				
			
			
				commit
				
					
						05e9af089c
					
				
			
		
					 61 changed files with 2597 additions and 757 deletions
				
			
		
							
								
								
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							
							
						
						
									
										3
									
								
								.gitignore
									
										
									
									
										vendored
									
									
								
							|  | @ -9,3 +9,6 @@ cp.out | ||||||
| 
 | 
 | ||||||
| # exclude node_modules if installed | # exclude node_modules if installed | ||||||
| /web/source/node_modules | /web/source/node_modules | ||||||
|  | 
 | ||||||
|  | # exclude compiled mkdocs site | ||||||
|  | site/ | ||||||
|  |  | ||||||
							
								
								
									
										9
									
								
								.readthedocs.yaml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								.readthedocs.yaml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | ||||||
|  | # .readthedocs.yaml | ||||||
|  | # Read the Docs configuration file | ||||||
|  | # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details | ||||||
|  | 
 | ||||||
|  | # Required | ||||||
|  | version: 2 | ||||||
|  | 
 | ||||||
|  | mkdocs: | ||||||
|  |   configuration: mkdocs.yml | ||||||
|  | @ -116,6 +116,7 @@ For bugs and feature requests, please check to see if there's [already an issue] | ||||||
| The following libraries and frameworks are used by GoToSocial, with gratitude 💕 | The following libraries and frameworks are used by GoToSocial, with gratitude 💕 | ||||||
| 
 | 
 | ||||||
| * [buckket/go-blurhash](https://github.com/buckket/go-blurhash); used for generating image blurhashes. [GPL-3.0 License](https://spdx.org/licenses/GPL-3.0-only.html). | * [buckket/go-blurhash](https://github.com/buckket/go-blurhash); used for generating image blurhashes. [GPL-3.0 License](https://spdx.org/licenses/GPL-3.0-only.html). | ||||||
|  | * [coreos/go-oidc](https://github.com/coreos/go-oidc); OIDC client library. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html). | ||||||
| * [gin-gonic/gin](https://github.com/gin-gonic/gin); speedy router engine. [MIT License](https://spdx.org/licenses/MIT.html). | * [gin-gonic/gin](https://github.com/gin-gonic/gin); speedy router engine. [MIT License](https://spdx.org/licenses/MIT.html). | ||||||
|   * [gin-contrib/cors](https://github.com/gin-contrib/cors); Gin CORS middleware. [MIT License](https://spdx.org/licenses/MIT.html). |   * [gin-contrib/cors](https://github.com/gin-contrib/cors); Gin CORS middleware. [MIT License](https://spdx.org/licenses/MIT.html). | ||||||
|   * [gin-contrib/sessions](https://github.com/gin-contrib/sessions); Gin sessions middleware. [MIT License](https://spdx.org/licenses/MIT.html) |   * [gin-contrib/sessions](https://github.com/gin-contrib/sessions); Gin sessions middleware. [MIT License](https://spdx.org/licenses/MIT.html) | ||||||
|  | @ -127,6 +128,7 @@ The following libraries and frameworks are used by GoToSocial, with gratitude  | ||||||
| * [gorilla/websocket](https://github.com/gorilla/websocket); Websocket connectivity. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html). | * [gorilla/websocket](https://github.com/gorilla/websocket); Websocket connectivity. [BSD-2-Clause License](https://spdx.org/licenses/BSD-2-Clause.html). | ||||||
| * [h2non/filetype](https://github.com/h2non/filetype); filetype checking. [MIT License](https://spdx.org/licenses/MIT.html). | * [h2non/filetype](https://github.com/h2non/filetype); filetype checking. [MIT License](https://spdx.org/licenses/MIT.html). | ||||||
| * [microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday); HTML user-input sanitization. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html). | * [microcosm-cc/bluemonday](https://github.com/microcosm-cc/bluemonday); HTML user-input sanitization. [BSD-3-Clause License](https://spdx.org/licenses/BSD-3-Clause.html). | ||||||
|  | * [nfnt/resize](https://github.com/nfnt/resize); convenient image resizing. [ISC License](https://spdx.org/licenses/ISC.html). | ||||||
| * [oklog/ulid](https://github.com/oklog/ulid); sequential, database-friendly ID generation. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html). | * [oklog/ulid](https://github.com/oklog/ulid); sequential, database-friendly ID generation. [Apache-2.0 License](https://spdx.org/licenses/Apache-2.0.html). | ||||||
| * [sirupsen/logrus](https://github.com/sirupsen/logrus); logging. [MIT License](https://spdx.org/licenses/MIT.html). | * [sirupsen/logrus](https://github.com/sirupsen/logrus); logging. [MIT License](https://spdx.org/licenses/MIT.html). | ||||||
| * [stretchr/testify](https://github.com/stretchr/testify); test framework. [MIT License](https://spdx.org/licenses/MIT.html). | * [stretchr/testify](https://github.com/stretchr/testify); test framework. [MIT License](https://spdx.org/licenses/MIT.html). | ||||||
|  |  | ||||||
							
								
								
									
										47
									
								
								cmd/gotosocial/accountsflags.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								cmd/gotosocial/accountsflags.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func accountsFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag { | ||||||
|  | 	return []cli.Flag{ | ||||||
|  | 		&cli.BoolFlag{ | ||||||
|  | 			Name:    flagNames.AccountsOpenRegistration, | ||||||
|  | 			Usage:   "Allow anyone to submit an account signup request. If false, server will be invite-only.", | ||||||
|  | 			Value:   defaults.AccountsOpenRegistration, | ||||||
|  | 			EnvVars: []string{envNames.AccountsOpenRegistration}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.BoolFlag{ | ||||||
|  | 			Name:    flagNames.AccountsApprovalRequired, | ||||||
|  | 			Usage:   "Do account signups require approval by an admin or moderator before user can log in? If false, new registrations will be automatically approved.", | ||||||
|  | 			Value:   defaults.AccountsRequireApproval, | ||||||
|  | 			EnvVars: []string{envNames.AccountsApprovalRequired}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.BoolFlag{ | ||||||
|  | 			Name:    flagNames.AccountsReasonRequired, | ||||||
|  | 			Usage:   "Do new account signups require a reason to be submitted on registration?", | ||||||
|  | 			Value:   defaults.AccountsReasonRequired, | ||||||
|  | 			EnvVars: []string{envNames.AccountsReasonRequired}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										128
									
								
								cmd/gotosocial/admincommands.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										128
									
								
								cmd/gotosocial/admincommands.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,128 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/cliactions/admin/account" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func adminCommands() []*cli.Command { | ||||||
|  | 	return []*cli.Command{ | ||||||
|  | 		{ | ||||||
|  | 			Name:  "admin", | ||||||
|  | 			Usage: "gotosocial admin-related tasks", | ||||||
|  | 			Subcommands: []*cli.Command{ | ||||||
|  | 				{ | ||||||
|  | 					Name:  "account", | ||||||
|  | 					Usage: "admin commands related to accounts", | ||||||
|  | 					Subcommands: []*cli.Command{ | ||||||
|  | 						{ | ||||||
|  | 							Name:  "create", | ||||||
|  | 							Usage: "create a new account", | ||||||
|  | 							Flags: []cli.Flag{ | ||||||
|  | 								&cli.StringFlag{ | ||||||
|  | 									Name:  config.UsernameFlag, | ||||||
|  | 									Usage: config.UsernameUsage, | ||||||
|  | 								}, | ||||||
|  | 								&cli.StringFlag{ | ||||||
|  | 									Name:  config.EmailFlag, | ||||||
|  | 									Usage: config.EmailUsage, | ||||||
|  | 								}, | ||||||
|  | 								&cli.StringFlag{ | ||||||
|  | 									Name:  config.PasswordFlag, | ||||||
|  | 									Usage: config.PasswordUsage, | ||||||
|  | 								}, | ||||||
|  | 							}, | ||||||
|  | 							Action: func(c *cli.Context) error { | ||||||
|  | 								return runAction(c, account.Create) | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 						{ | ||||||
|  | 							Name:  "confirm", | ||||||
|  | 							Usage: "confirm an existing account manually, thereby skipping email confirmation", | ||||||
|  | 							Flags: []cli.Flag{ | ||||||
|  | 								&cli.StringFlag{ | ||||||
|  | 									Name:  config.UsernameFlag, | ||||||
|  | 									Usage: config.UsernameUsage, | ||||||
|  | 								}, | ||||||
|  | 							}, | ||||||
|  | 							Action: func(c *cli.Context) error { | ||||||
|  | 								return runAction(c, account.Confirm) | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 						{ | ||||||
|  | 							Name:  "promote", | ||||||
|  | 							Usage: "promote an account to admin", | ||||||
|  | 							Flags: []cli.Flag{ | ||||||
|  | 								&cli.StringFlag{ | ||||||
|  | 									Name:  config.UsernameFlag, | ||||||
|  | 									Usage: config.UsernameUsage, | ||||||
|  | 								}, | ||||||
|  | 							}, | ||||||
|  | 							Action: func(c *cli.Context) error { | ||||||
|  | 								return runAction(c, account.Promote) | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 						{ | ||||||
|  | 							Name:  "demote", | ||||||
|  | 							Usage: "demote an account from admin to normal user", | ||||||
|  | 							Flags: []cli.Flag{ | ||||||
|  | 								&cli.StringFlag{ | ||||||
|  | 									Name:  config.UsernameFlag, | ||||||
|  | 									Usage: config.UsernameUsage, | ||||||
|  | 								}, | ||||||
|  | 							}, | ||||||
|  | 							Action: func(c *cli.Context) error { | ||||||
|  | 								return runAction(c, account.Demote) | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 						{ | ||||||
|  | 							Name:  "disable", | ||||||
|  | 							Usage: "prevent an account from signing in or posting etc, but don't delete anything", | ||||||
|  | 							Flags: []cli.Flag{ | ||||||
|  | 								&cli.StringFlag{ | ||||||
|  | 									Name:  config.UsernameFlag, | ||||||
|  | 									Usage: config.UsernameUsage, | ||||||
|  | 								}, | ||||||
|  | 							}, | ||||||
|  | 							Action: func(c *cli.Context) error { | ||||||
|  | 								return runAction(c, account.Disable) | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 						{ | ||||||
|  | 							Name:  "suspend", | ||||||
|  | 							Usage: "completely remove an account and all of its posts, media, etc", | ||||||
|  | 							Flags: []cli.Flag{ | ||||||
|  | 								&cli.StringFlag{ | ||||||
|  | 									Name:  config.UsernameFlag, | ||||||
|  | 									Usage: config.UsernameUsage, | ||||||
|  | 								}, | ||||||
|  | 							}, | ||||||
|  | 							Action: func(c *cli.Context) error { | ||||||
|  | 								return runAction(c, account.Suspend) | ||||||
|  | 							}, | ||||||
|  | 						}, | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										37
									
								
								cmd/gotosocial/commands.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								cmd/gotosocial/commands.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func getCommands() []*cli.Command { | ||||||
|  | 	commands := []*cli.Command{} | ||||||
|  | 	commandSets := [][]*cli.Command{ | ||||||
|  | 		serverCommands(), | ||||||
|  | 		adminCommands(), | ||||||
|  | 		testrigCommands(), | ||||||
|  | 	} | ||||||
|  | 	for _, cs := range commandSets { | ||||||
|  | 		commands = append(commands, cs...) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return commands | ||||||
|  | } | ||||||
							
								
								
									
										77
									
								
								cmd/gotosocial/databaseflags.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								cmd/gotosocial/databaseflags.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func databaseFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag { | ||||||
|  | 	return []cli.Flag{ | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.DbType, | ||||||
|  | 			Usage:   "Database type: eg., postgres", | ||||||
|  | 			Value:   defaults.DbType, | ||||||
|  | 			EnvVars: []string{envNames.DbType}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.DbAddress, | ||||||
|  | 			Usage:   "Database ipv4 address or hostname", | ||||||
|  | 			Value:   defaults.DbAddress, | ||||||
|  | 			EnvVars: []string{envNames.DbAddress}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.IntFlag{ | ||||||
|  | 			Name:    flagNames.DbPort, | ||||||
|  | 			Usage:   "Database port", | ||||||
|  | 			Value:   defaults.DbPort, | ||||||
|  | 			EnvVars: []string{envNames.DbPort}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.DbUser, | ||||||
|  | 			Usage:   "Database username", | ||||||
|  | 			Value:   defaults.DbUser, | ||||||
|  | 			EnvVars: []string{envNames.DbUser}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.DbPassword, | ||||||
|  | 			Usage:   "Database password", | ||||||
|  | 			Value:   defaults.DbPassword, | ||||||
|  | 			EnvVars: []string{envNames.DbPassword}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.DbDatabase, | ||||||
|  | 			Usage:   "Database name", | ||||||
|  | 			Value:   defaults.DbDatabase, | ||||||
|  | 			EnvVars: []string{envNames.DbDatabase}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.DbTLSMode, | ||||||
|  | 			Usage:   "Database tls mode", | ||||||
|  | 			Value:   defaults.DBTlsMode, | ||||||
|  | 			EnvVars: []string{envNames.DbTLSMode}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.DbTLSCACert, | ||||||
|  | 			Usage:   "Path to CA cert for db tls connection", | ||||||
|  | 			Value:   defaults.DBTlsCACert, | ||||||
|  | 			EnvVars: []string{envNames.DbTLSCACert}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										48
									
								
								cmd/gotosocial/flags.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										48
									
								
								cmd/gotosocial/flags.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,48 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func getFlags() []cli.Flag { | ||||||
|  | 	flagNames := config.GetFlagNames() | ||||||
|  | 	envNames := config.GetEnvNames() | ||||||
|  | 	defaults := config.GetDefaults() | ||||||
|  | 
 | ||||||
|  | 	flags := []cli.Flag{} | ||||||
|  | 	flagSets := [][]cli.Flag{ | ||||||
|  | 		generalFlags(flagNames, envNames, defaults), | ||||||
|  | 		databaseFlags(flagNames, envNames, defaults), | ||||||
|  | 		templateFlags(flagNames, envNames, defaults), | ||||||
|  | 		accountsFlags(flagNames, envNames, defaults), | ||||||
|  | 		mediaFlags(flagNames, envNames, defaults), | ||||||
|  | 		storageFlags(flagNames, envNames, defaults), | ||||||
|  | 		statusesFlags(flagNames, envNames, defaults), | ||||||
|  | 		letsEncryptFlags(flagNames, envNames, defaults), | ||||||
|  | 		oidcFlags(flagNames, envNames, defaults), | ||||||
|  | 	} | ||||||
|  | 	for _, fs := range flagSets { | ||||||
|  | 		flags = append(flags, fs...) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return flags | ||||||
|  | } | ||||||
							
								
								
									
										66
									
								
								cmd/gotosocial/generalflags.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								cmd/gotosocial/generalflags.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func generalFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag { | ||||||
|  | 	return []cli.Flag{ | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.LogLevel, | ||||||
|  | 			Usage:   "Log level to run at: debug, info, warn, fatal", | ||||||
|  | 			Value:   defaults.LogLevel, | ||||||
|  | 			EnvVars: []string{envNames.LogLevel}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.ApplicationName, | ||||||
|  | 			Usage:   "Name of the application, used in various places internally", | ||||||
|  | 			Value:   defaults.ApplicationName, | ||||||
|  | 			EnvVars: []string{envNames.ApplicationName}, | ||||||
|  | 			Hidden:  true, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.ConfigPath, | ||||||
|  | 			Usage:   "Path to a yaml file containing gotosocial configuration. Values set in this file will be overwritten by values set as env vars or arguments", | ||||||
|  | 			Value:   defaults.ConfigPath, | ||||||
|  | 			EnvVars: []string{envNames.ConfigPath}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.Host, | ||||||
|  | 			Usage:   "Hostname to use for the server (eg., example.org, gotosocial.whatever.com). DO NOT change this on a server that's already run!", | ||||||
|  | 			Value:   defaults.Host, | ||||||
|  | 			EnvVars: []string{envNames.Host}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.AccountDomain, | ||||||
|  | 			Usage:   "Domain to use in account names (eg., example.org, whatever.com). If not set, will default to the setting for host. DO NOT change this on a server that's already run!", | ||||||
|  | 			Value:   defaults.AccountDomain, | ||||||
|  | 			EnvVars: []string{envNames.AccountDomain}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.Protocol, | ||||||
|  | 			Usage:   "Protocol to use for the REST api of the server (only use http for debugging and tests!)", | ||||||
|  | 			Value:   defaults.Protocol, | ||||||
|  | 			EnvVars: []string{envNames.Protocol}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										47
									
								
								cmd/gotosocial/letsencryptflags.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								cmd/gotosocial/letsencryptflags.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func letsEncryptFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag { | ||||||
|  | 	return []cli.Flag{ | ||||||
|  | 		&cli.BoolFlag{ | ||||||
|  | 			Name:    flagNames.LetsEncryptEnabled, | ||||||
|  | 			Usage:   "Enable letsencrypt TLS certs for this server. If set to true, then cert dir also needs to be set (or take the default).", | ||||||
|  | 			Value:   defaults.LetsEncryptEnabled, | ||||||
|  | 			EnvVars: []string{envNames.LetsEncryptEnabled}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.LetsEncryptCertDir, | ||||||
|  | 			Usage:   "Directory to store acquired letsencrypt certificates.", | ||||||
|  | 			Value:   defaults.LetsEncryptCertDir, | ||||||
|  | 			EnvVars: []string{envNames.LetsEncryptCertDir}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.LetsEncryptEmailAddress, | ||||||
|  | 			Usage:   "Email address to use when requesting letsencrypt certs. Will receive updates on cert expiry etc.", | ||||||
|  | 			Value:   defaults.LetsEncryptEmailAddress, | ||||||
|  | 			EnvVars: []string{envNames.LetsEncryptEmailAddress}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -19,16 +19,9 @@ | ||||||
| package main | package main | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
| 	"os" | 	"os" | ||||||
| 
 | 
 | ||||||
| 	"github.com/sirupsen/logrus" | 	"github.com/sirupsen/logrus" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/cliactions" |  | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/cliactions/admin/account" |  | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/cliactions/server" |  | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/cliactions/testrig" |  | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" |  | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/log" |  | ||||||
| 
 | 
 | ||||||
| 	"github.com/urfave/cli/v2" | 	"github.com/urfave/cli/v2" | ||||||
| ) | ) | ||||||
|  | @ -40,372 +33,18 @@ var Version string | ||||||
| var Commit string | var Commit string | ||||||
| 
 | 
 | ||||||
| func main() { | func main() { | ||||||
| 	flagNames := config.GetFlagNames() | 	var v string | ||||||
| 	envNames := config.GetEnvNames() | 	if Commit == "" { | ||||||
| 	defaults := config.GetDefaults() | 		v = Version | ||||||
|  | 	} else { | ||||||
|  | 		v = Version + " " + Commit[:7] | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	app := &cli.App{ | 	app := &cli.App{ | ||||||
| 		Version: Version + " " + Commit[:7], | 		Version:  v, | ||||||
| 		Usage:   "a fediverse social media server", | 		Usage:    "a fediverse social media server", | ||||||
| 		Flags: []cli.Flag{ | 		Flags:    getFlags(), | ||||||
| 			// GENERAL FLAGS | 		Commands: getCommands(), | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.LogLevel, |  | ||||||
| 				Usage:   "Log level to run at: debug, info, warn, fatal", |  | ||||||
| 				Value:   defaults.LogLevel, |  | ||||||
| 				EnvVars: []string{envNames.LogLevel}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.ApplicationName, |  | ||||||
| 				Usage:   "Name of the application, used in various places internally", |  | ||||||
| 				Value:   defaults.ApplicationName, |  | ||||||
| 				EnvVars: []string{envNames.ApplicationName}, |  | ||||||
| 				Hidden:  true, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.ConfigPath, |  | ||||||
| 				Usage:   "Path to a yaml file containing gotosocial configuration. Values set in this file will be overwritten by values set as env vars or arguments", |  | ||||||
| 				Value:   defaults.ConfigPath, |  | ||||||
| 				EnvVars: []string{envNames.ConfigPath}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.Host, |  | ||||||
| 				Usage:   "Hostname to use for the server (eg., example.org, gotosocial.whatever.com). DO NOT change this on a server that's already run!", |  | ||||||
| 				Value:   defaults.Host, |  | ||||||
| 				EnvVars: []string{envNames.Host}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.AccountDomain, |  | ||||||
| 				Usage:   "Domain to use in account names (eg., example.org, whatever.com). If not set, will default to the setting for host. DO NOT change this on a server that's already run!", |  | ||||||
| 				Value:   defaults.AccountDomain, |  | ||||||
| 				EnvVars: []string{envNames.AccountDomain}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.Protocol, |  | ||||||
| 				Usage:   "Protocol to use for the REST api of the server (only use http for debugging and tests!)", |  | ||||||
| 				Value:   defaults.Protocol, |  | ||||||
| 				EnvVars: []string{envNames.Protocol}, |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 			// DATABASE FLAGS |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.DbType, |  | ||||||
| 				Usage:   "Database type: eg., postgres", |  | ||||||
| 				Value:   defaults.DbType, |  | ||||||
| 				EnvVars: []string{envNames.DbType}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.DbAddress, |  | ||||||
| 				Usage:   "Database ipv4 address or hostname", |  | ||||||
| 				Value:   defaults.DbAddress, |  | ||||||
| 				EnvVars: []string{envNames.DbAddress}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.IntFlag{ |  | ||||||
| 				Name:    flagNames.DbPort, |  | ||||||
| 				Usage:   "Database port", |  | ||||||
| 				Value:   defaults.DbPort, |  | ||||||
| 				EnvVars: []string{envNames.DbPort}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.DbUser, |  | ||||||
| 				Usage:   "Database username", |  | ||||||
| 				Value:   defaults.DbUser, |  | ||||||
| 				EnvVars: []string{envNames.DbUser}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.DbPassword, |  | ||||||
| 				Usage:   "Database password", |  | ||||||
| 				Value:   defaults.DbPassword, |  | ||||||
| 				EnvVars: []string{envNames.DbPassword}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.DbDatabase, |  | ||||||
| 				Usage:   "Database name", |  | ||||||
| 				Value:   defaults.DbDatabase, |  | ||||||
| 				EnvVars: []string{envNames.DbDatabase}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.DbTLSMode, |  | ||||||
| 				Usage:   "Database tls mode", |  | ||||||
| 				Value:   defaults.DBTlsMode, |  | ||||||
| 				EnvVars: []string{envNames.DbTLSMode}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.DbTLSCACert, |  | ||||||
| 				Usage:   "Path to CA cert for db tls connection", |  | ||||||
| 				Value:   defaults.DBTlsCACert, |  | ||||||
| 				EnvVars: []string{envNames.DbTLSCACert}, |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 			// TEMPLATE FLAGS |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.TemplateBaseDir, |  | ||||||
| 				Usage:   "Basedir for html templating files for rendering pages and composing emails.", |  | ||||||
| 				Value:   defaults.TemplateBaseDir, |  | ||||||
| 				EnvVars: []string{envNames.TemplateBaseDir}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.AssetBaseDir, |  | ||||||
| 				Usage:   "Directory to serve static assets from, accessible at example.com/assets/", |  | ||||||
| 				Value:   defaults.AssetBaseDir, |  | ||||||
| 				EnvVars: []string{envNames.AssetBaseDir}, |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 			// ACCOUNTS FLAGS |  | ||||||
| 			&cli.BoolFlag{ |  | ||||||
| 				Name:    flagNames.AccountsOpenRegistration, |  | ||||||
| 				Usage:   "Allow anyone to submit an account signup request. If false, server will be invite-only.", |  | ||||||
| 				Value:   defaults.AccountsOpenRegistration, |  | ||||||
| 				EnvVars: []string{envNames.AccountsOpenRegistration}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.BoolFlag{ |  | ||||||
| 				Name:    flagNames.AccountsApprovalRequired, |  | ||||||
| 				Usage:   "Do account signups require approval by an admin or moderator before user can log in? If false, new registrations will be automatically approved.", |  | ||||||
| 				Value:   defaults.AccountsRequireApproval, |  | ||||||
| 				EnvVars: []string{envNames.AccountsApprovalRequired}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.BoolFlag{ |  | ||||||
| 				Name:    flagNames.AccountsReasonRequired, |  | ||||||
| 				Usage:   "Do new account signups require a reason to be submitted on registration?", |  | ||||||
| 				Value:   defaults.AccountsReasonRequired, |  | ||||||
| 				EnvVars: []string{envNames.AccountsReasonRequired}, |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 			// MEDIA FLAGS |  | ||||||
| 			&cli.IntFlag{ |  | ||||||
| 				Name:    flagNames.MediaMaxImageSize, |  | ||||||
| 				Usage:   "Max size of accepted images in bytes", |  | ||||||
| 				Value:   defaults.MediaMaxImageSize, |  | ||||||
| 				EnvVars: []string{envNames.MediaMaxImageSize}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.IntFlag{ |  | ||||||
| 				Name:    flagNames.MediaMaxVideoSize, |  | ||||||
| 				Usage:   "Max size of accepted videos in bytes", |  | ||||||
| 				Value:   defaults.MediaMaxVideoSize, |  | ||||||
| 				EnvVars: []string{envNames.MediaMaxVideoSize}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.IntFlag{ |  | ||||||
| 				Name:    flagNames.MediaMinDescriptionChars, |  | ||||||
| 				Usage:   "Min required chars for an image description", |  | ||||||
| 				Value:   defaults.MediaMinDescriptionChars, |  | ||||||
| 				EnvVars: []string{envNames.MediaMinDescriptionChars}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.IntFlag{ |  | ||||||
| 				Name:    flagNames.MediaMaxDescriptionChars, |  | ||||||
| 				Usage:   "Max permitted chars for an image description", |  | ||||||
| 				Value:   defaults.MediaMaxDescriptionChars, |  | ||||||
| 				EnvVars: []string{envNames.MediaMaxDescriptionChars}, |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 			// STORAGE FLAGS |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.StorageBackend, |  | ||||||
| 				Usage:   "Storage backend to use for media attachments", |  | ||||||
| 				Value:   defaults.StorageBackend, |  | ||||||
| 				EnvVars: []string{envNames.StorageBackend}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.StorageBasePath, |  | ||||||
| 				Usage:   "Full path to an already-created directory where gts should store/retrieve media files. Subfolders will be created within this dir.", |  | ||||||
| 				Value:   defaults.StorageBasePath, |  | ||||||
| 				EnvVars: []string{envNames.StorageBasePath}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.StorageServeProtocol, |  | ||||||
| 				Usage:   "Protocol to use for serving media attachments (use https if storage is local)", |  | ||||||
| 				Value:   defaults.StorageServeProtocol, |  | ||||||
| 				EnvVars: []string{envNames.StorageServeProtocol}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.StorageServeHost, |  | ||||||
| 				Usage:   "Hostname to serve media attachments from (use the same value as host if storage is local)", |  | ||||||
| 				Value:   defaults.StorageServeHost, |  | ||||||
| 				EnvVars: []string{envNames.StorageServeHost}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.StorageServeBasePath, |  | ||||||
| 				Usage:   "Path to append to protocol and hostname to create the base path from which media files will be served (default will mostly be fine)", |  | ||||||
| 				Value:   defaults.StorageServeBasePath, |  | ||||||
| 				EnvVars: []string{envNames.StorageServeBasePath}, |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 			// STATUSES FLAGS |  | ||||||
| 			&cli.IntFlag{ |  | ||||||
| 				Name:    flagNames.StatusesMaxChars, |  | ||||||
| 				Usage:   "Max permitted characters for posted statuses", |  | ||||||
| 				Value:   defaults.StatusesMaxChars, |  | ||||||
| 				EnvVars: []string{envNames.StatusesMaxChars}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.IntFlag{ |  | ||||||
| 				Name:    flagNames.StatusesCWMaxChars, |  | ||||||
| 				Usage:   "Max permitted characters for content/spoiler warnings on statuses", |  | ||||||
| 				Value:   defaults.StatusesCWMaxChars, |  | ||||||
| 				EnvVars: []string{envNames.StatusesCWMaxChars}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.IntFlag{ |  | ||||||
| 				Name:    flagNames.StatusesPollMaxOptions, |  | ||||||
| 				Usage:   "Max amount of options permitted on a poll", |  | ||||||
| 				Value:   defaults.StatusesPollMaxOptions, |  | ||||||
| 				EnvVars: []string{envNames.StatusesPollMaxOptions}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.IntFlag{ |  | ||||||
| 				Name:    flagNames.StatusesPollOptionMaxChars, |  | ||||||
| 				Usage:   "Max amount of characters for a poll option", |  | ||||||
| 				Value:   defaults.StatusesPollOptionMaxChars, |  | ||||||
| 				EnvVars: []string{envNames.StatusesPollOptionMaxChars}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.IntFlag{ |  | ||||||
| 				Name:    flagNames.StatusesMaxMediaFiles, |  | ||||||
| 				Usage:   "Maximum number of media files/attachments per status", |  | ||||||
| 				Value:   defaults.StatusesMaxMediaFiles, |  | ||||||
| 				EnvVars: []string{envNames.StatusesMaxMediaFiles}, |  | ||||||
| 			}, |  | ||||||
| 
 |  | ||||||
| 			// LETSENCRYPT FLAGS |  | ||||||
| 			&cli.BoolFlag{ |  | ||||||
| 				Name:    flagNames.LetsEncryptEnabled, |  | ||||||
| 				Usage:   "Enable letsencrypt TLS certs for this server. If set to true, then cert dir also needs to be set (or take the default).", |  | ||||||
| 				Value:   defaults.LetsEncryptEnabled, |  | ||||||
| 				EnvVars: []string{envNames.LetsEncryptEnabled}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.LetsEncryptCertDir, |  | ||||||
| 				Usage:   "Directory to store acquired letsencrypt certificates.", |  | ||||||
| 				Value:   defaults.LetsEncryptCertDir, |  | ||||||
| 				EnvVars: []string{envNames.LetsEncryptCertDir}, |  | ||||||
| 			}, |  | ||||||
| 			&cli.StringFlag{ |  | ||||||
| 				Name:    flagNames.LetsEncryptEmailAddress, |  | ||||||
| 				Usage:   "Email address to use when requesting letsencrypt certs. Will receive updates on cert expiry etc.", |  | ||||||
| 				Value:   defaults.LetsEncryptEmailAddress, |  | ||||||
| 				EnvVars: []string{envNames.LetsEncryptEmailAddress}, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 		Commands: []*cli.Command{ |  | ||||||
| 			{ |  | ||||||
| 				Name:  "server", |  | ||||||
| 				Usage: "gotosocial server-related tasks", |  | ||||||
| 				Subcommands: []*cli.Command{ |  | ||||||
| 					{ |  | ||||||
| 						Name:  "start", |  | ||||||
| 						Usage: "start the gotosocial server", |  | ||||||
| 						Action: func(c *cli.Context) error { |  | ||||||
| 							return runAction(c, server.Start) |  | ||||||
| 						}, |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 			{ |  | ||||||
| 				Name:  "admin", |  | ||||||
| 				Usage: "gotosocial admin-related tasks", |  | ||||||
| 				Subcommands: []*cli.Command{ |  | ||||||
| 					{ |  | ||||||
| 						Name:  "account", |  | ||||||
| 						Usage: "admin commands related to accounts", |  | ||||||
| 						Subcommands: []*cli.Command{ |  | ||||||
| 							{ |  | ||||||
| 								Name:  "create", |  | ||||||
| 								Usage: "create a new account", |  | ||||||
| 								Flags: []cli.Flag{ |  | ||||||
| 									&cli.StringFlag{ |  | ||||||
| 										Name:  config.UsernameFlag, |  | ||||||
| 										Usage: config.UsernameUsage, |  | ||||||
| 									}, |  | ||||||
| 									&cli.StringFlag{ |  | ||||||
| 										Name:  config.EmailFlag, |  | ||||||
| 										Usage: config.EmailUsage, |  | ||||||
| 									}, |  | ||||||
| 									&cli.StringFlag{ |  | ||||||
| 										Name:  config.PasswordFlag, |  | ||||||
| 										Usage: config.PasswordUsage, |  | ||||||
| 									}, |  | ||||||
| 								}, |  | ||||||
| 								Action: func(c *cli.Context) error { |  | ||||||
| 									return runAction(c, account.Create) |  | ||||||
| 								}, |  | ||||||
| 							}, |  | ||||||
| 							{ |  | ||||||
| 								Name:  "confirm", |  | ||||||
| 								Usage: "confirm an existing account manually, thereby skipping email confirmation", |  | ||||||
| 								Flags: []cli.Flag{ |  | ||||||
| 									&cli.StringFlag{ |  | ||||||
| 										Name:  config.UsernameFlag, |  | ||||||
| 										Usage: config.UsernameUsage, |  | ||||||
| 									}, |  | ||||||
| 								}, |  | ||||||
| 								Action: func(c *cli.Context) error { |  | ||||||
| 									return runAction(c, account.Confirm) |  | ||||||
| 								}, |  | ||||||
| 							}, |  | ||||||
| 							{ |  | ||||||
| 								Name:  "promote", |  | ||||||
| 								Usage: "promote an account to admin", |  | ||||||
| 								Flags: []cli.Flag{ |  | ||||||
| 									&cli.StringFlag{ |  | ||||||
| 										Name:  config.UsernameFlag, |  | ||||||
| 										Usage: config.UsernameUsage, |  | ||||||
| 									}, |  | ||||||
| 								}, |  | ||||||
| 								Action: func(c *cli.Context) error { |  | ||||||
| 									return runAction(c, account.Promote) |  | ||||||
| 								}, |  | ||||||
| 							}, |  | ||||||
| 							{ |  | ||||||
| 								Name:  "demote", |  | ||||||
| 								Usage: "demote an account from admin to normal user", |  | ||||||
| 								Flags: []cli.Flag{ |  | ||||||
| 									&cli.StringFlag{ |  | ||||||
| 										Name:  config.UsernameFlag, |  | ||||||
| 										Usage: config.UsernameUsage, |  | ||||||
| 									}, |  | ||||||
| 								}, |  | ||||||
| 								Action: func(c *cli.Context) error { |  | ||||||
| 									return runAction(c, account.Demote) |  | ||||||
| 								}, |  | ||||||
| 							}, |  | ||||||
| 							{ |  | ||||||
| 								Name:  "disable", |  | ||||||
| 								Usage: "prevent an account from signing in or posting etc, but don't delete anything", |  | ||||||
| 								Flags: []cli.Flag{ |  | ||||||
| 									&cli.StringFlag{ |  | ||||||
| 										Name:  config.UsernameFlag, |  | ||||||
| 										Usage: config.UsernameUsage, |  | ||||||
| 									}, |  | ||||||
| 								}, |  | ||||||
| 								Action: func(c *cli.Context) error { |  | ||||||
| 									return runAction(c, account.Disable) |  | ||||||
| 								}, |  | ||||||
| 							}, |  | ||||||
| 							{ |  | ||||||
| 								Name:  "suspend", |  | ||||||
| 								Usage: "completely remove an account and all of its posts, media, etc", |  | ||||||
| 								Flags: []cli.Flag{ |  | ||||||
| 									&cli.StringFlag{ |  | ||||||
| 										Name:  config.UsernameFlag, |  | ||||||
| 										Usage: config.UsernameUsage, |  | ||||||
| 									}, |  | ||||||
| 								}, |  | ||||||
| 								Action: func(c *cli.Context) error { |  | ||||||
| 									return runAction(c, account.Suspend) |  | ||||||
| 								}, |  | ||||||
| 							}, |  | ||||||
| 						}, |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 			{ |  | ||||||
| 				Name:  "testrig", |  | ||||||
| 				Usage: "gotosocial testrig tasks", |  | ||||||
| 				Subcommands: []*cli.Command{ |  | ||||||
| 					{ |  | ||||||
| 						Name:  "start", |  | ||||||
| 						Usage: "start the gotosocial testrig", |  | ||||||
| 						Action: func(c *cli.Context) error { |  | ||||||
| 							return runAction(c, testrig.Start) |  | ||||||
| 						}, |  | ||||||
| 					}, |  | ||||||
| 				}, |  | ||||||
| 			}, |  | ||||||
| 		}, |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	err := app.Run(os.Args) | 	err := app.Run(os.Args) | ||||||
|  | @ -413,26 +52,3 @@ func main() { | ||||||
| 		logrus.Fatal(err) | 		logrus.Fatal(err) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 |  | ||||||
| // runAction builds up the config and logger necessary for any |  | ||||||
| // gotosocial action, and then executes the action. |  | ||||||
| func runAction(c *cli.Context, a cliactions.GTSAction) error { |  | ||||||
| 
 |  | ||||||
| 	// create a new *config.Config based on the config path provided... |  | ||||||
| 	conf, err := config.FromFile(c.String(config.GetFlagNames().ConfigPath)) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("error creating config: %s", err) |  | ||||||
| 	} |  | ||||||
| 	// ... and the flags set on the *cli.Context by urfave |  | ||||||
| 	if err := conf.ParseCLIFlags(c, c.App.Version); err != nil { |  | ||||||
| 		return fmt.Errorf("error parsing config: %s", err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	// create a logger with the log level, formatting, and output splitter already set |  | ||||||
| 	log, err := log.New(conf.LogLevel) |  | ||||||
| 	if err != nil { |  | ||||||
| 		return fmt.Errorf("error creating logger: %s", err) |  | ||||||
| 	} |  | ||||||
| 
 |  | ||||||
| 	return a(c.Context, conf, log) |  | ||||||
| } |  | ||||||
|  |  | ||||||
							
								
								
									
										53
									
								
								cmd/gotosocial/mediaflags.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										53
									
								
								cmd/gotosocial/mediaflags.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,53 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func mediaFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag { | ||||||
|  | 	return []cli.Flag{ | ||||||
|  | 		&cli.IntFlag{ | ||||||
|  | 			Name:    flagNames.MediaMaxImageSize, | ||||||
|  | 			Usage:   "Max size of accepted images in bytes", | ||||||
|  | 			Value:   defaults.MediaMaxImageSize, | ||||||
|  | 			EnvVars: []string{envNames.MediaMaxImageSize}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.IntFlag{ | ||||||
|  | 			Name:    flagNames.MediaMaxVideoSize, | ||||||
|  | 			Usage:   "Max size of accepted videos in bytes", | ||||||
|  | 			Value:   defaults.MediaMaxVideoSize, | ||||||
|  | 			EnvVars: []string{envNames.MediaMaxVideoSize}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.IntFlag{ | ||||||
|  | 			Name:    flagNames.MediaMinDescriptionChars, | ||||||
|  | 			Usage:   "Min required chars for an image description", | ||||||
|  | 			Value:   defaults.MediaMinDescriptionChars, | ||||||
|  | 			EnvVars: []string{envNames.MediaMinDescriptionChars}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.IntFlag{ | ||||||
|  | 			Name:    flagNames.MediaMaxDescriptionChars, | ||||||
|  | 			Usage:   "Max permitted chars for an image description", | ||||||
|  | 			Value:   defaults.MediaMaxDescriptionChars, | ||||||
|  | 			EnvVars: []string{envNames.MediaMaxDescriptionChars}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										71
									
								
								cmd/gotosocial/oidcflags.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								cmd/gotosocial/oidcflags.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,71 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func oidcFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag { | ||||||
|  | 	return []cli.Flag{ | ||||||
|  | 		&cli.BoolFlag{ | ||||||
|  | 			Name:    flagNames.OIDCEnabled, | ||||||
|  | 			Usage:   "Enabled OIDC authorization for this instance. If set to true, then the other OIDC flags must also be set.", | ||||||
|  | 			Value:   defaults.OIDCEnabled, | ||||||
|  | 			EnvVars: []string{envNames.OIDCEnabled}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.OIDCIdpName, | ||||||
|  | 			Usage:   "Name of the OIDC identity provider. Will be shown to the user when logging in.", | ||||||
|  | 			Value:   defaults.OIDCIdpName, | ||||||
|  | 			EnvVars: []string{envNames.OIDCIdpName}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.BoolFlag{ | ||||||
|  | 			Name:    flagNames.OIDCSkipVerification, | ||||||
|  | 			Usage:   "Skip verification of tokens returned by the OIDC provider. Should only be set to 'true' for testing purposes, never in a production environment!", | ||||||
|  | 			Value:   defaults.OIDCSkipVerification, | ||||||
|  | 			EnvVars: []string{envNames.OIDCSkipVerification}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.OIDCIssuer, | ||||||
|  | 			Usage:   "Address of the OIDC issuer. Should be the web address, including protocol, at which the issuer can be reached. Eg., 'https://example.org/auth'", | ||||||
|  | 			Value:   defaults.OIDCIssuer, | ||||||
|  | 			EnvVars: []string{envNames.OIDCIssuer}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.OIDCClientID, | ||||||
|  | 			Usage:   "ClientID of GoToSocial, as registered with the OIDC provider.", | ||||||
|  | 			Value:   defaults.OIDCClientID, | ||||||
|  | 			EnvVars: []string{envNames.OIDCClientID}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.OIDCClientSecret, | ||||||
|  | 			Usage:   "ClientSecret of GoToSocial, as registered with the OIDC provider.", | ||||||
|  | 			Value:   defaults.OIDCClientSecret, | ||||||
|  | 			EnvVars: []string{envNames.OIDCClientSecret}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringSliceFlag{ | ||||||
|  | 			Name:    flagNames.OIDCScopes, | ||||||
|  | 			Usage:   "ClientSecret of GoToSocial, as registered with the OIDC provider.", | ||||||
|  | 			Value:   cli.NewStringSlice(defaults.OIDCScopes...), | ||||||
|  | 			EnvVars: []string{envNames.OIDCScopes}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										51
									
								
								cmd/gotosocial/runaction.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										51
									
								
								cmd/gotosocial/runaction.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,51 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/cliactions" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/log" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // runAction builds up the config and logger necessary for any | ||||||
|  | // gotosocial action, and then executes the action. | ||||||
|  | func runAction(c *cli.Context, a cliactions.GTSAction) error { | ||||||
|  | 
 | ||||||
|  | 	// create a new *config.Config based on the config path provided... | ||||||
|  | 	conf, err := config.FromFile(c.String(config.GetFlagNames().ConfigPath)) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("error creating config: %s", err) | ||||||
|  | 	} | ||||||
|  | 	// ... and the flags set on the *cli.Context by urfave | ||||||
|  | 	if err := conf.ParseCLIFlags(c, c.App.Version); err != nil { | ||||||
|  | 		return fmt.Errorf("error parsing config: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// create a logger with the log level, formatting, and output splitter already set | ||||||
|  | 	log, err := log.New(conf.LogLevel) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("error creating logger: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return a(c.Context, conf, log) | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								cmd/gotosocial/servercommands.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								cmd/gotosocial/servercommands.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/cliactions/server" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func serverCommands() []*cli.Command { | ||||||
|  | 	return []*cli.Command{ | ||||||
|  | 		{ | ||||||
|  | 			Name:  "server", | ||||||
|  | 			Usage: "gotosocial server-related tasks", | ||||||
|  | 			Subcommands: []*cli.Command{ | ||||||
|  | 				{ | ||||||
|  | 					Name:  "start", | ||||||
|  | 					Usage: "start the gotosocial server", | ||||||
|  | 					Action: func(c *cli.Context) error { | ||||||
|  | 						return runAction(c, server.Start) | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								cmd/gotosocial/statusesflags.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								cmd/gotosocial/statusesflags.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func statusesFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag { | ||||||
|  | 	return []cli.Flag{ | ||||||
|  | 		&cli.IntFlag{ | ||||||
|  | 			Name:    flagNames.StatusesMaxChars, | ||||||
|  | 			Usage:   "Max permitted characters for posted statuses", | ||||||
|  | 			Value:   defaults.StatusesMaxChars, | ||||||
|  | 			EnvVars: []string{envNames.StatusesMaxChars}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.IntFlag{ | ||||||
|  | 			Name:    flagNames.StatusesCWMaxChars, | ||||||
|  | 			Usage:   "Max permitted characters for content/spoiler warnings on statuses", | ||||||
|  | 			Value:   defaults.StatusesCWMaxChars, | ||||||
|  | 			EnvVars: []string{envNames.StatusesCWMaxChars}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.IntFlag{ | ||||||
|  | 			Name:    flagNames.StatusesPollMaxOptions, | ||||||
|  | 			Usage:   "Max amount of options permitted on a poll", | ||||||
|  | 			Value:   defaults.StatusesPollMaxOptions, | ||||||
|  | 			EnvVars: []string{envNames.StatusesPollMaxOptions}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.IntFlag{ | ||||||
|  | 			Name:    flagNames.StatusesPollOptionMaxChars, | ||||||
|  | 			Usage:   "Max amount of characters for a poll option", | ||||||
|  | 			Value:   defaults.StatusesPollOptionMaxChars, | ||||||
|  | 			EnvVars: []string{envNames.StatusesPollOptionMaxChars}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.IntFlag{ | ||||||
|  | 			Name:    flagNames.StatusesMaxMediaFiles, | ||||||
|  | 			Usage:   "Maximum number of media files/attachments per status", | ||||||
|  | 			Value:   defaults.StatusesMaxMediaFiles, | ||||||
|  | 			EnvVars: []string{envNames.StatusesMaxMediaFiles}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										59
									
								
								cmd/gotosocial/storageflags.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								cmd/gotosocial/storageflags.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,59 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func storageFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag { | ||||||
|  | 	return []cli.Flag{ | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.StorageBackend, | ||||||
|  | 			Usage:   "Storage backend to use for media attachments", | ||||||
|  | 			Value:   defaults.StorageBackend, | ||||||
|  | 			EnvVars: []string{envNames.StorageBackend}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.StorageBasePath, | ||||||
|  | 			Usage:   "Full path to an already-created directory where gts should store/retrieve media files. Subfolders will be created within this dir.", | ||||||
|  | 			Value:   defaults.StorageBasePath, | ||||||
|  | 			EnvVars: []string{envNames.StorageBasePath}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.StorageServeProtocol, | ||||||
|  | 			Usage:   "Protocol to use for serving media attachments (use https if storage is local)", | ||||||
|  | 			Value:   defaults.StorageServeProtocol, | ||||||
|  | 			EnvVars: []string{envNames.StorageServeProtocol}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.StorageServeHost, | ||||||
|  | 			Usage:   "Hostname to serve media attachments from (use the same value as host if storage is local)", | ||||||
|  | 			Value:   defaults.StorageServeHost, | ||||||
|  | 			EnvVars: []string{envNames.StorageServeHost}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.StorageServeBasePath, | ||||||
|  | 			Usage:   "Path to append to protocol and hostname to create the base path from which media files will be served (default will mostly be fine)", | ||||||
|  | 			Value:   defaults.StorageServeBasePath, | ||||||
|  | 			EnvVars: []string{envNames.StorageServeBasePath}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										41
									
								
								cmd/gotosocial/templateflags.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										41
									
								
								cmd/gotosocial/templateflags.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,41 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func templateFlags(flagNames, envNames config.Flags, defaults config.Defaults) []cli.Flag { | ||||||
|  | 	return []cli.Flag{ | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.TemplateBaseDir, | ||||||
|  | 			Usage:   "Basedir for html templating files for rendering pages and composing emails.", | ||||||
|  | 			Value:   defaults.TemplateBaseDir, | ||||||
|  | 			EnvVars: []string{envNames.TemplateBaseDir}, | ||||||
|  | 		}, | ||||||
|  | 		&cli.StringFlag{ | ||||||
|  | 			Name:    flagNames.AssetBaseDir, | ||||||
|  | 			Usage:   "Directory to serve static assets from, accessible at example.com/assets/", | ||||||
|  | 			Value:   defaults.AssetBaseDir, | ||||||
|  | 			EnvVars: []string{envNames.AssetBaseDir}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
							
								
								
									
										42
									
								
								cmd/gotosocial/testrigcommands.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								cmd/gotosocial/testrigcommands.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package main | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/cliactions/testrig" | ||||||
|  | 	"github.com/urfave/cli/v2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func testrigCommands() []*cli.Command { | ||||||
|  | 	return []*cli.Command{ | ||||||
|  | 		{ | ||||||
|  | 			Name:  "testrig", | ||||||
|  | 			Usage: "gotosocial testrig tasks", | ||||||
|  | 			Subcommands: []*cli.Command{ | ||||||
|  | 				{ | ||||||
|  | 					Name:  "start", | ||||||
|  | 					Usage: "start the gotosocial testrig", | ||||||
|  | 					Action: func(c *cli.Context) error { | ||||||
|  | 						return runAction(c, testrig.Start) | ||||||
|  | 					}, | ||||||
|  | 				}, | ||||||
|  | 			}, | ||||||
|  | 		}, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -1,20 +0,0 @@ | ||||||
| # Minimal makefile for Sphinx documentation
 |  | ||||||
| #
 |  | ||||||
| 
 |  | ||||||
| # You can set these variables from the command line, and also
 |  | ||||||
| # from the environment for the first two.
 |  | ||||||
| SPHINXOPTS    ?= |  | ||||||
| SPHINXBUILD   ?= sphinx-build |  | ||||||
| SOURCEDIR     = . |  | ||||||
| BUILDDIR      = _build |  | ||||||
| 
 |  | ||||||
| # Put it first so that "make" without argument is like "make help".
 |  | ||||||
| help: |  | ||||||
| 	@$(SPHINXBUILD) -M help "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) |  | ||||||
| 
 |  | ||||||
| .PHONY: help Makefile |  | ||||||
| 
 |  | ||||||
| # Catch-all target: route all unknown targets to Sphinx using the new
 |  | ||||||
| # "make mode" option.  $(O) is meant as a shortcut for $(SPHINXOPTS).
 |  | ||||||
| %: Makefile |  | ||||||
| 	@$(SPHINXBUILD) -M $@ "$(SOURCEDIR)" "$(BUILDDIR)" $(SPHINXOPTS) $(O) |  | ||||||
							
								
								
									
										52
									
								
								docs/conf.py
									
										
									
									
									
								
							
							
						
						
									
										52
									
								
								docs/conf.py
									
										
									
									
									
								
							|  | @ -1,52 +0,0 @@ | ||||||
| # Configuration file for the Sphinx documentation builder. |  | ||||||
| # |  | ||||||
| # This file only contains a selection of the most common options. For a full |  | ||||||
| # list see the documentation: |  | ||||||
| # https://www.sphinx-doc.org/en/master/usage/configuration.html |  | ||||||
| 
 |  | ||||||
| # -- Path setup -------------------------------------------------------------- |  | ||||||
| 
 |  | ||||||
| # If extensions (or modules to document with autodoc) are in another directory, |  | ||||||
| # add these directories to sys.path here. If the directory is relative to the |  | ||||||
| # documentation root, use os.path.abspath to make it absolute, like shown here. |  | ||||||
| # |  | ||||||
| # import os |  | ||||||
| # import sys |  | ||||||
| # sys.path.insert(0, os.path.abspath('.')) |  | ||||||
| 
 |  | ||||||
| import sphinx_rtd_theme |  | ||||||
| 
 |  | ||||||
| # -- Project information ----------------------------------------------------- |  | ||||||
| 
 |  | ||||||
| project = 'GoToSocial' |  | ||||||
| copyright = '2021, GoToSocial Authors' |  | ||||||
| author = 'GoToSocial Authors' |  | ||||||
| 
 |  | ||||||
| # -- General configuration --------------------------------------------------- |  | ||||||
| 
 |  | ||||||
| # Add any Sphinx extension module names here, as strings. They can be |  | ||||||
| # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom |  | ||||||
| # ones. |  | ||||||
| extensions = [ |  | ||||||
|     "sphinx_rtd_theme" |  | ||||||
| ] |  | ||||||
| 
 |  | ||||||
| # Add any paths that contain templates here, relative to this directory. |  | ||||||
| templates_path = ['_templates'] |  | ||||||
| 
 |  | ||||||
| # List of patterns, relative to source directory, that match files and |  | ||||||
| # directories to ignore when looking for source files. |  | ||||||
| # This pattern also affects html_static_path and html_extra_path. |  | ||||||
| exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] |  | ||||||
| 
 |  | ||||||
| # -- Options for HTML output ------------------------------------------------- |  | ||||||
| 
 |  | ||||||
| # The theme to use for HTML and HTML Help pages.  See the documentation for |  | ||||||
| # a list of builtin themes. |  | ||||||
| # |  | ||||||
| html_theme = "sphinx_rtd_theme" |  | ||||||
| 
 |  | ||||||
| # Add any paths that contain custom static files (such as style sheets) here, |  | ||||||
| # relative to this directory. They are copied after the builtin static files, |  | ||||||
| # so a file named "default.css" will overwrite the builtin "default.css". |  | ||||||
| html_static_path = ['_static'] |  | ||||||
							
								
								
									
										1
									
								
								docs/configuration/basics.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docs/configuration/basics.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | # How to Manage Configuration | ||||||
							
								
								
									
										1
									
								
								docs/configuration/general.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								docs/configuration/general.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1 @@ | ||||||
|  | # General | ||||||
							
								
								
									
										164
									
								
								docs/configuration/oidc.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										164
									
								
								docs/configuration/oidc.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,164 @@ | ||||||
|  | # OpenID Connect (OIDC) | ||||||
|  | 
 | ||||||
|  | GoToSocial supports [OpenID Connect](https://openid.net/connect/), which is an identification protocol built on top of [OAuth 2.0](https://oauth.net/2/), an industry standard protocol for authorization. | ||||||
|  | 
 | ||||||
|  | This means that you can connect GoToSocial to an external OIDC provider like [Gitlab](https://docs.gitlab.com/ee/integration/openid_connect_provider.html), [Google](https://cloud.google.com/identity-platform/docs/web/oidc), [Keycloak](https://www.keycloak.org/), or [Dex](https://dexidp.io/) and allow users to sign in to GoToSocial using their credentials for that provider. | ||||||
|  | 
 | ||||||
|  | This is very convenient in the following cases: | ||||||
|  | 
 | ||||||
|  | - You're running multiple services on a platform (Matrix, Peertube, GoToSocial), and you want users to be able to use the same sign in page for all of them. | ||||||
|  | - You want to delegate management of users, accounts, passwords etc. to an external service to make admin easier for yourself. | ||||||
|  | - You already have a lot of users in an external system and you don't want to have to recreate them all in GoToSocial manually. | ||||||
|  | 
 | ||||||
|  | ## Settings | ||||||
|  | 
 | ||||||
|  | GoToSocial exposes the following configuration settings for OIDC, shown below with their default values. | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | ####################### | ||||||
|  | ##### OIDC CONFIG ##### | ||||||
|  | ####################### | ||||||
|  | 
 | ||||||
|  | # Config for authentication with an external OIDC provider (Dex, Google, Auth0, etc). | ||||||
|  | oidc: | ||||||
|  | 
 | ||||||
|  |   # Bool. Enable authentication with external OIDC provider. If set to true, then | ||||||
|  |   # the other OIDC options must be set as well. If this is set to false, then the standard | ||||||
|  |   # internal oauth flow will be used, where users sign in to GtS with username/password. | ||||||
|  |   # Options: [true, false] | ||||||
|  |   # Default: false | ||||||
|  |   enabled: false | ||||||
|  | 
 | ||||||
|  |   # String. Name of the oidc idp (identity provider). This will be shown to users when | ||||||
|  |   # they log in. | ||||||
|  |   # Examples: ["Google", "Dex", "Auth0"] | ||||||
|  |   # Default: "" | ||||||
|  |   idpName: "" | ||||||
|  | 
 | ||||||
|  |   # Bool. Skip the normal verification flow of tokens returned from the OIDC provider, ie., | ||||||
|  |   # don't check the expiry or signature. This should only be used in debugging or testing, | ||||||
|  |   # never ever in a production environment as it's extremely unsafe! | ||||||
|  |   # Options: [true, false] | ||||||
|  |   # Default: false | ||||||
|  |   skipVerification: false | ||||||
|  | 
 | ||||||
|  |   # String. The OIDC issuer URI. This is where GtS will redirect users to for login. | ||||||
|  |   # Typically this will look like a standard web URL. | ||||||
|  |   # Examples: ["https://auth.example.org", "https://example.org/auth"] | ||||||
|  |   # Default: "" | ||||||
|  |   issuer: "" | ||||||
|  | 
 | ||||||
|  |   # String. The ID for this client as registered with the OIDC provider. | ||||||
|  |   # Examples: ["some-client-id", "fda3772a-ad35-41c9-9a59-f1943ad18f54"] | ||||||
|  |   # Default: "" | ||||||
|  |   clientID: "" | ||||||
|  | 
 | ||||||
|  |   # String. The secret for this client as registered with the OIDC provider. | ||||||
|  |   # Examples: ["super-secret-business", "79379cf5-8057-426d-bb83-af504d98a7b0"] | ||||||
|  |   # Default: "" | ||||||
|  |   clientSecret: "" | ||||||
|  | 
 | ||||||
|  |   # Array of string. Scopes to request from the OIDC provider. The returned values will be used to | ||||||
|  |   # populate users created in GtS as a result of the authentication flow. 'openid' and 'email' are required. | ||||||
|  |   # 'profile' is used to extract a username for the newly created user. | ||||||
|  |   # 'groups' is optional and can be used to determine if a user is an admin (if they're in the group 'admin' or 'admins'). | ||||||
|  |   # Examples: See eg., https://auth0.com/docs/scopes/openid-connect-scopes | ||||||
|  |   # Default: ["openid", "email", "profile", "groups"] | ||||||
|  |   scopes: | ||||||
|  |     - "openid" | ||||||
|  |     - "email" | ||||||
|  |     - "profile" | ||||||
|  |     - "groups" | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | ## Behavior | ||||||
|  | 
 | ||||||
|  | When OIDC is enabled on GoToSocial, the default sign-in page redirects automatically to the sign-in page for the OIDC provider. | ||||||
|  | 
 | ||||||
|  | This means that OIDC essentially *replaces* the normal GtS email/password sign-in flow. | ||||||
|  | 
 | ||||||
|  | When a user logs in through OIDC, GoToSocial will request that user's preferred email address and username from the OIDC provider. It will then use the returned email address to either: | ||||||
|  | 
 | ||||||
|  | *If the email address is already associated with a user/account*: sign the requester in as that user/account. | ||||||
|  | 
 | ||||||
|  | Or: | ||||||
|  | 
 | ||||||
|  | *If the email address is not yet associated with a user/account*: create a new user and account with the returned credentials, and sign the requester in as that user/account. | ||||||
|  | 
 | ||||||
|  | In other words, GoToSocial completely delegates sign-in authority to the OIDC provider, and trusts whatever credentials it returns. | ||||||
|  | 
 | ||||||
|  | ### Username conflicts | ||||||
|  | 
 | ||||||
|  | In some cases, such as when a server has been switched to use OIDC after already using default settings for a while, there may be an overlap between usernames returned from OIDC, and usernames that already existed in the database. | ||||||
|  | 
 | ||||||
|  | For example, let's say that someone with username `gordonbrownfan` and email address `gordon_is_best@example.org` has an account on a GtS instance that uses the default sign-in flow. | ||||||
|  | 
 | ||||||
|  | That GtS instance then switches to using OIDC login. However, in the OIDC's storage there's also a user with username `gordonbrownfan`. If this user has the email address `gordon_is_best@example.org`, then GoToSocial will assume that the two users are the same and just log `gordonbrownfan` in as though nothing had changed. No problem! | ||||||
|  | 
 | ||||||
|  | However, if the user in the OIDC storage has a different email address, GoToSocial will try to create a new user and account for this person. | ||||||
|  | 
 | ||||||
|  | Since the username `gordonbrownfan` is already taken, GoToSocial will try `gordonbrownfan1`. If this is also taken, it will try `gordonbrownfan2`, and so on, until it finds a username that's not yet taken. It will then sign the requester in as that user/account, distinct from the original `gordonbrownfan`. | ||||||
|  | 
 | ||||||
|  | ### Malformed usernames | ||||||
|  | 
 | ||||||
|  | A username returned from an OIDC provider might not always fit the pattern of what GoToSocial accepts as a valid username, ie., lower-case letters, numbers, and underscores. In this case, GoToSocial will do its best to parse the returned username into something that fits the pattern. | ||||||
|  | 
 | ||||||
|  | For example, say that an OIDC provider returns the username `Marx Is Great` for a sign in, which doesn't fit the pattern because it contains upper-case letters and spaces. | ||||||
|  | 
 | ||||||
|  | In this case, GtS will convert it into `marx_is_great` by applying the following rules: | ||||||
|  | 
 | ||||||
|  | 1. Trim any leading or trailing whitespace. | ||||||
|  | 2. Convert all letters to lowercase. | ||||||
|  | 3. Replace spaces with underscores. | ||||||
|  | 
 | ||||||
|  | Unfortunately, at this point GoToSocial doesn't know how to handle returned usernames containing special characters such as `@` or `%`, so these will return an error. | ||||||
|  | 
 | ||||||
|  | ### Group membership | ||||||
|  | 
 | ||||||
|  | Most OIDC providers allow for the concept of groups and group memberships in returned claims. GoToSocial can use group membership to determine whether or not a user returned from an OIDC flow should be created as an admin account or not. | ||||||
|  | 
 | ||||||
|  | If the returned OIDC groups information for a user contains membership of the groups `admin` or `admins`, then that user will be created/signed in as though they are an admin. | ||||||
|  | 
 | ||||||
|  | ## Provider Examples | ||||||
|  | 
 | ||||||
|  | ### Dex | ||||||
|  | 
 | ||||||
|  | [Dex](https://dexidp.io/) is an open-source OIDC Provider that you can host yourself. The procedure for installing Dex is out of scope for this documentation, but you can check out the Dex docs [here](https://dexidp.io/docs/). | ||||||
|  | 
 | ||||||
|  | Dex is great because it's also written in Go, like GoToSocial, which means it's small and fast and works well on lower-powered systems. | ||||||
|  | 
 | ||||||
|  | To configure Dex and GoToSocial to work together, create the following client under the `staticClients` section of your Dex config: | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | staticClients: | ||||||
|  |   - id: 'gotosocial_client' | ||||||
|  |     redirectURIs: | ||||||
|  |       - 'https://gotosocial.example.org/auth/callback' | ||||||
|  |     name: 'GoToSocial' | ||||||
|  |     secret: 'some-client-secret' | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Make sure to replace `gotosocial_client` with your desired client ID, and `secret` with a reasonably long and secure secret (a UUID for example). You should also replace `gotosocial.example.org` with the `host` of your GtS instance, but leave `/auth/callback` in place. | ||||||
|  | 
 | ||||||
|  | Now, edit the `oidc` section of your GoToSocial config.yaml as follows: | ||||||
|  | 
 | ||||||
|  | ```yaml | ||||||
|  | oidc: | ||||||
|  |   enabled: true | ||||||
|  |   idpName: "Dex" | ||||||
|  |   skipVerification: false | ||||||
|  |   issuer: "https://auth.example.org" | ||||||
|  |   clientID: "gotosocial_client" | ||||||
|  |   clientSecret: "some-client-secret" | ||||||
|  |   scopes: | ||||||
|  |     - "openid" | ||||||
|  |     - "email" | ||||||
|  |     - "profile" | ||||||
|  |     - "groups" | ||||||
|  | ``` | ||||||
|  | 
 | ||||||
|  | Make sure to replace the `issuer` variable with whatever your Dex issuer is set to. This should be the exact URI at which your Dex instance can be reached. | ||||||
|  | 
 | ||||||
|  | Now, restart both GoToSocial and Dex so that the new settings are in place. | ||||||
|  | 
 | ||||||
|  | When you next go to log in to GtS, you should be redirected to the sign in page for Dex. On a successful sign in, you'll be directed back to GoToSocial. | ||||||
|  | @ -1,49 +0,0 @@ | ||||||
| name: gotosocial-docs |  | ||||||
| channels: |  | ||||||
|   - defaults |  | ||||||
| dependencies: |  | ||||||
|   - _libgcc_mutex=0.1=main |  | ||||||
|   - ca-certificates=2021.1.19=h06a4308_0 |  | ||||||
|   - certifi=2020.12.5=py39h06a4308_0 |  | ||||||
|   - ld_impl_linux-64=2.33.1=h53a641e_7 |  | ||||||
|   - libedit=3.1.20191231=h14c3975_1 |  | ||||||
|   - libffi=3.3=he6710b0_2 |  | ||||||
|   - libgcc-ng=9.1.0=hdf63c60_0 |  | ||||||
|   - libstdcxx-ng=9.1.0=hdf63c60_0 |  | ||||||
|   - ncurses=6.2=he6710b0_1 |  | ||||||
|   - openssl=1.1.1j=h27cfd23_0 |  | ||||||
|   - pip=21.0.1=py39h06a4308_0 |  | ||||||
|   - python=3.9.1=hdb3f193_2 |  | ||||||
|   - readline=8.1=h27cfd23_0 |  | ||||||
|   - setuptools=52.0.0=py39h06a4308_0 |  | ||||||
|   - sqlite=3.33.0=h62c20be_0 |  | ||||||
|   - tk=8.6.10=hbc83047_0 |  | ||||||
|   - tzdata=2020f=h52ac0ba_0 |  | ||||||
|   - wheel=0.36.2=pyhd3eb1b0_0 |  | ||||||
|   - xz=5.2.5=h7b6447c_0 |  | ||||||
|   - zlib=1.2.11=h7b6447c_3 |  | ||||||
|   - pip: |  | ||||||
|     - alabaster==0.7.12 |  | ||||||
|     - babel==2.9.0 |  | ||||||
|     - chardet==4.0.0 |  | ||||||
|     - docutils==0.16 |  | ||||||
|     - idna==2.10 |  | ||||||
|     - imagesize==1.2.0 |  | ||||||
|     - jinja2==2.11.3 |  | ||||||
|     - markupsafe==1.1.1 |  | ||||||
|     - packaging==20.9 |  | ||||||
|     - pygments==2.8.0 |  | ||||||
|     - pyparsing==2.4.7 |  | ||||||
|     - pytz==2021.1 |  | ||||||
|     - requests==2.25.1 |  | ||||||
|     - snowballstemmer==2.1.0 |  | ||||||
|     - sphinx==3.5.1 |  | ||||||
|     - sphinx-rtd-theme==0.5.1 |  | ||||||
|     - sphinxcontrib-applehelp==1.0.2 |  | ||||||
|     - sphinxcontrib-devhelp==1.0.2 |  | ||||||
|     - sphinxcontrib-htmlhelp==1.0.3 |  | ||||||
|     - sphinxcontrib-jsmath==1.0.1 |  | ||||||
|     - sphinxcontrib-qthelp==1.0.3 |  | ||||||
|     - sphinxcontrib-serializinghtml==1.1.4 |  | ||||||
|     - urllib3==1.26.3 |  | ||||||
| 
 |  | ||||||
							
								
								
									
										57
									
								
								docs/index.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										57
									
								
								docs/index.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,57 @@ | ||||||
|  | # What is GoToSocial? | ||||||
|  | 
 | ||||||
|  | GoToSocial provides a lightweight, customizable, and safety-focused entryway into the [Fediverse](https://en.wikipedia.org/wiki/Fediverse), and is comparable to (but distinct from) existing projects such as [Mastodon](https://joinmastodon.org/), [Pleroma](https://pleroma.social/), [Friendica](https://friendica.net), and [PixelFed](https://pixelfed.org/). | ||||||
|  | 
 | ||||||
|  | With GoToSocial, you can keep in touch with your friends, post, read, and share images and articles, without being tracked or advertised to. | ||||||
|  | 
 | ||||||
|  | ## Federation | ||||||
|  | 
 | ||||||
|  | Because GoToSocial uses the [ActivityPub](https://activitypub.rocks/) protocol, you can Keep in touch not only with people on your home server, but with people all over the [Fediverse](https://en.wikipedia.org/wiki/Fediverse), seamlessly! | ||||||
|  | 
 | ||||||
|  | ## Mastodon API compatible | ||||||
|  | 
 | ||||||
|  | Full support for modern, elegant apps like [Tusky](https://tusky.app/) and [Pinafore](https://pinafore.social/). | ||||||
|  | 
 | ||||||
|  | ## Granular post settings | ||||||
|  | 
 | ||||||
|  | You should be able to choose how your posts can be interacted with: | ||||||
|  | 
 | ||||||
|  | * Local-only posts. | ||||||
|  | * Rebloggable/boostable toggle. | ||||||
|  | * 'Likeable' toggle. | ||||||
|  | * 'Replyable' toggle. | ||||||
|  | 
 | ||||||
|  | ## Easy customizability for admins | ||||||
|  | 
 | ||||||
|  | * Adjustable post length. | ||||||
|  | * Media upload size settings. | ||||||
|  | 
 | ||||||
|  | ## LetsEncrypt | ||||||
|  | 
 | ||||||
|  |  Built-in, automatic support for secure HTTPS with [LetsEncrypt](https://letsencrypt.org/). | ||||||
|  | 
 | ||||||
|  | ## Light footprint and good performance | ||||||
|  | 
 | ||||||
|  | Plays nice with lower-powered machines like Raspberry Pi, old laptops and tiny VPSes. | ||||||
|  | 
 | ||||||
|  | ## Easy to deploy | ||||||
|  | 
 | ||||||
|  | No external dependencies apart from a database. Just download the binary + assets (or Docker container), and run. | ||||||
|  | 
 | ||||||
|  | ## HTTP signature authentication | ||||||
|  | 
 | ||||||
|  | Protect your data. | ||||||
|  | 
 | ||||||
|  | ## User Safety | ||||||
|  | 
 | ||||||
|  | Strict privacy enforcement for posts and strict blocking logic. | ||||||
|  | 
 | ||||||
|  | ## Subscribeable and shareable allow/denylists for federation | ||||||
|  | 
 | ||||||
|  | Import and export allowlists and denylists. Subscribe to community-created blocklists (think Adblocker, but for federation!). | ||||||
|  | 
 | ||||||
|  | ## Various federation modes | ||||||
|  | 
 | ||||||
|  | * 'Normal' federation; discover new servers. | ||||||
|  | * Allowlist-only federation; choose which servers you talk to. | ||||||
|  | * Zero federation; keep your server private. | ||||||
|  | @ -1,28 +0,0 @@ | ||||||
| .. GoToSocial documentation master file, created by |  | ||||||
|    sphinx-quickstart on Sun Feb 28 12:01:26 2021. |  | ||||||
|    You can adapt this file completely to your liking, but it should at least |  | ||||||
|    contain the root `toctree` directive. |  | ||||||
| 
 |  | ||||||
| Welcome to GoToSocial's documentation! |  | ||||||
| ====================================== |  | ||||||
| 
 |  | ||||||
| .. toctree:: |  | ||||||
|    :maxdepth: 2 |  | ||||||
|    :hidden: |  | ||||||
|    :caption: About GoToSocial |  | ||||||
| 
 |  | ||||||
|    pages/about/index |  | ||||||
| 
 |  | ||||||
| .. toctree:: |  | ||||||
|    :maxdepth: 2 |  | ||||||
|    :hidden: |  | ||||||
|    :caption: Design |  | ||||||
| 
 |  | ||||||
|    pages/design/index |  | ||||||
| 
 |  | ||||||
| .. toctree:: |  | ||||||
|    :maxdepth: 2 |  | ||||||
|    :hidden: |  | ||||||
|    :caption: Developer Docs |  | ||||||
| 
 |  | ||||||
|    pages/api/index |  | ||||||
							
								
								
									
										3
									
								
								docs/installation_guide/docker.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								docs/installation_guide/docker.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,3 @@ | ||||||
|  | # Docker | ||||||
|  | 
 | ||||||
|  | Installing with Docker.... | ||||||
							
								
								
									
										669
									
								
								docs/installation_guide/license.md
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										669
									
								
								docs/installation_guide/license.md
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,669 @@ | ||||||
|  | # License | ||||||
|  | 
 | ||||||
|  | GoToSocial is licensed under the [GNU AFFERO GENERAL PUBLIC LICENSE](https://www.gnu.org/licenses/agpl-3.0.html), the full text of which is given below. | ||||||
|  | 
 | ||||||
|  | Copyright (C) 2021 [GoToSocial Authors](mailto:admin@gotosocial.org) | ||||||
|  | 
 | ||||||
|  | ```license | ||||||
|  |                     GNU AFFERO GENERAL PUBLIC LICENSE | ||||||
|  |                        Version 3, 19 November 2007 | ||||||
|  | 
 | ||||||
|  |  Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/> | ||||||
|  |  Everyone is permitted to copy and distribute verbatim copies | ||||||
|  |  of this license document, but changing it is not allowed. | ||||||
|  | 
 | ||||||
|  |                             Preamble | ||||||
|  | 
 | ||||||
|  |   The GNU Affero General Public License is a free, copyleft license for | ||||||
|  | software and other kinds of works, specifically designed to ensure | ||||||
|  | cooperation with the community in the case of network server software. | ||||||
|  | 
 | ||||||
|  |   The licenses for most software and other practical works are designed | ||||||
|  | to take away your freedom to share and change the works.  By contrast, | ||||||
|  | our General Public Licenses are intended to guarantee your freedom to | ||||||
|  | share and change all versions of a program--to make sure it remains free | ||||||
|  | software for all its users. | ||||||
|  | 
 | ||||||
|  |   When we speak of free software, we are referring to freedom, not | ||||||
|  | price.  Our General Public Licenses are designed to make sure that you | ||||||
|  | have the freedom to distribute copies of free software (and charge for | ||||||
|  | them if you wish), that you receive source code or can get it if you | ||||||
|  | want it, that you can change the software or use pieces of it in new | ||||||
|  | free programs, and that you know you can do these things. | ||||||
|  | 
 | ||||||
|  |   Developers that use our General Public Licenses protect your rights | ||||||
|  | with two steps: (1) assert copyright on the software, and (2) offer | ||||||
|  | you this License which gives you legal permission to copy, distribute | ||||||
|  | and/or modify the software. | ||||||
|  | 
 | ||||||
|  |   A secondary benefit of defending all users' freedom is that | ||||||
|  | improvements made in alternate versions of the program, if they | ||||||
|  | receive widespread use, become available for other developers to | ||||||
|  | incorporate.  Many developers of free software are heartened and | ||||||
|  | encouraged by the resulting cooperation.  However, in the case of | ||||||
|  | software used on network servers, this result may fail to come about. | ||||||
|  | The GNU General Public License permits making a modified version and | ||||||
|  | letting the public access it on a server without ever releasing its | ||||||
|  | source code to the public. | ||||||
|  | 
 | ||||||
|  |   The GNU Affero General Public License is designed specifically to | ||||||
|  | ensure that, in such cases, the modified source code becomes available | ||||||
|  | to the community.  It requires the operator of a network server to | ||||||
|  | provide the source code of the modified version running there to the | ||||||
|  | users of that server.  Therefore, public use of a modified version, on | ||||||
|  | a publicly accessible server, gives the public access to the source | ||||||
|  | code of the modified version. | ||||||
|  | 
 | ||||||
|  |   An older license, called the Affero General Public License and | ||||||
|  | published by Affero, was designed to accomplish similar goals.  This is | ||||||
|  | a different license, not a version of the Affero GPL, but Affero has | ||||||
|  | released a new version of the Affero GPL which permits relicensing under | ||||||
|  | this license. | ||||||
|  | 
 | ||||||
|  |   The precise terms and conditions for copying, distribution and | ||||||
|  | modification follow. | ||||||
|  | 
 | ||||||
|  |                        TERMS AND CONDITIONS | ||||||
|  | 
 | ||||||
|  |   0. Definitions. | ||||||
|  | 
 | ||||||
|  |   "This License" refers to version 3 of the GNU Affero General Public License. | ||||||
|  | 
 | ||||||
|  |   "Copyright" also means copyright-like laws that apply to other kinds of | ||||||
|  | works, such as semiconductor masks. | ||||||
|  | 
 | ||||||
|  |   "The Program" refers to any copyrightable work licensed under this | ||||||
|  | License.  Each licensee is addressed as "you".  "Licensees" and | ||||||
|  | "recipients" may be individuals or organizations. | ||||||
|  | 
 | ||||||
|  |   To "modify" a work means to copy from or adapt all or part of the work | ||||||
|  | in a fashion requiring copyright permission, other than the making of an | ||||||
|  | exact copy.  The resulting work is called a "modified version" of the | ||||||
|  | earlier work or a work "based on" the earlier work. | ||||||
|  | 
 | ||||||
|  |   A "covered work" means either the unmodified Program or a work based | ||||||
|  | on the Program. | ||||||
|  | 
 | ||||||
|  |   To "propagate" a work means to do anything with it that, without | ||||||
|  | permission, would make you directly or secondarily liable for | ||||||
|  | infringement under applicable copyright law, except executing it on a | ||||||
|  | computer or modifying a private copy.  Propagation includes copying, | ||||||
|  | distribution (with or without modification), making available to the | ||||||
|  | public, and in some countries other activities as well. | ||||||
|  | 
 | ||||||
|  |   To "convey" a work means any kind of propagation that enables other | ||||||
|  | parties to make or receive copies.  Mere interaction with a user through | ||||||
|  | a computer network, with no transfer of a copy, is not conveying. | ||||||
|  | 
 | ||||||
|  |   An interactive user interface displays "Appropriate Legal Notices" | ||||||
|  | to the extent that it includes a convenient and prominently visible | ||||||
|  | feature that (1) displays an appropriate copyright notice, and (2) | ||||||
|  | tells the user that there is no warranty for the work (except to the | ||||||
|  | extent that warranties are provided), that licensees may convey the | ||||||
|  | work under this License, and how to view a copy of this License.  If | ||||||
|  | the interface presents a list of user commands or options, such as a | ||||||
|  | menu, a prominent item in the list meets this criterion. | ||||||
|  | 
 | ||||||
|  |   1. Source Code. | ||||||
|  | 
 | ||||||
|  |   The "source code" for a work means the preferred form of the work | ||||||
|  | for making modifications to it.  "Object code" means any non-source | ||||||
|  | form of a work. | ||||||
|  | 
 | ||||||
|  |   A "Standard Interface" means an interface that either is an official | ||||||
|  | standard defined by a recognized standards body, or, in the case of | ||||||
|  | interfaces specified for a particular programming language, one that | ||||||
|  | is widely used among developers working in that language. | ||||||
|  | 
 | ||||||
|  |   The "System Libraries" of an executable work include anything, other | ||||||
|  | than the work as a whole, that (a) is included in the normal form of | ||||||
|  | packaging a Major Component, but which is not part of that Major | ||||||
|  | Component, and (b) serves only to enable use of the work with that | ||||||
|  | Major Component, or to implement a Standard Interface for which an | ||||||
|  | implementation is available to the public in source code form.  A | ||||||
|  | "Major Component", in this context, means a major essential component | ||||||
|  | (kernel, window system, and so on) of the specific operating system | ||||||
|  | (if any) on which the executable work runs, or a compiler used to | ||||||
|  | produce the work, or an object code interpreter used to run it. | ||||||
|  | 
 | ||||||
|  |   The "Corresponding Source" for a work in object code form means all | ||||||
|  | the source code needed to generate, install, and (for an executable | ||||||
|  | work) run the object code and to modify the work, including scripts to | ||||||
|  | control those activities.  However, it does not include the work's | ||||||
|  | System Libraries, or general-purpose tools or generally available free | ||||||
|  | programs which are used unmodified in performing those activities but | ||||||
|  | which are not part of the work.  For example, Corresponding Source | ||||||
|  | includes interface definition files associated with source files for | ||||||
|  | the work, and the source code for shared libraries and dynamically | ||||||
|  | linked subprograms that the work is specifically designed to require, | ||||||
|  | such as by intimate data communication or control flow between those | ||||||
|  | subprograms and other parts of the work. | ||||||
|  | 
 | ||||||
|  |   The Corresponding Source need not include anything that users | ||||||
|  | can regenerate automatically from other parts of the Corresponding | ||||||
|  | Source. | ||||||
|  | 
 | ||||||
|  |   The Corresponding Source for a work in source code form is that | ||||||
|  | same work. | ||||||
|  | 
 | ||||||
|  |   2. Basic Permissions. | ||||||
|  | 
 | ||||||
|  |   All rights granted under this License are granted for the term of | ||||||
|  | copyright on the Program, and are irrevocable provided the stated | ||||||
|  | conditions are met.  This License explicitly affirms your unlimited | ||||||
|  | permission to run the unmodified Program.  The output from running a | ||||||
|  | covered work is covered by this License only if the output, given its | ||||||
|  | content, constitutes a covered work.  This License acknowledges your | ||||||
|  | rights of fair use or other equivalent, as provided by copyright law. | ||||||
|  | 
 | ||||||
|  |   You may make, run and propagate covered works that you do not | ||||||
|  | convey, without conditions so long as your license otherwise remains | ||||||
|  | in force.  You may convey covered works to others for the sole purpose | ||||||
|  | of having them make modifications exclusively for you, or provide you | ||||||
|  | with facilities for running those works, provided that you comply with | ||||||
|  | the terms of this License in conveying all material for which you do | ||||||
|  | not control copyright.  Those thus making or running the covered works | ||||||
|  | for you must do so exclusively on your behalf, under your direction | ||||||
|  | and control, on terms that prohibit them from making any copies of | ||||||
|  | your copyrighted material outside their relationship with you. | ||||||
|  | 
 | ||||||
|  |   Conveying under any other circumstances is permitted solely under | ||||||
|  | the conditions stated below.  Sublicensing is not allowed; section 10 | ||||||
|  | makes it unnecessary. | ||||||
|  | 
 | ||||||
|  |   3. Protecting Users' Legal Rights From Anti-Circumvention Law. | ||||||
|  | 
 | ||||||
|  |   No covered work shall be deemed part of an effective technological | ||||||
|  | measure under any applicable law fulfilling obligations under article | ||||||
|  | 11 of the WIPO copyright treaty adopted on 20 December 1996, or | ||||||
|  | similar laws prohibiting or restricting circumvention of such | ||||||
|  | measures. | ||||||
|  | 
 | ||||||
|  |   When you convey a covered work, you waive any legal power to forbid | ||||||
|  | circumvention of technological measures to the extent such circumvention | ||||||
|  | is effected by exercising rights under this License with respect to | ||||||
|  | the covered work, and you disclaim any intention to limit operation or | ||||||
|  | modification of the work as a means of enforcing, against the work's | ||||||
|  | users, your or third parties' legal rights to forbid circumvention of | ||||||
|  | technological measures. | ||||||
|  | 
 | ||||||
|  |   4. Conveying Verbatim Copies. | ||||||
|  | 
 | ||||||
|  |   You may convey verbatim copies of the Program's source code as you | ||||||
|  | receive it, in any medium, provided that you conspicuously and | ||||||
|  | appropriately publish on each copy an appropriate copyright notice; | ||||||
|  | keep intact all notices stating that this License and any | ||||||
|  | non-permissive terms added in accord with section 7 apply to the code; | ||||||
|  | keep intact all notices of the absence of any warranty; and give all | ||||||
|  | recipients a copy of this License along with the Program. | ||||||
|  | 
 | ||||||
|  |   You may charge any price or no price for each copy that you convey, | ||||||
|  | and you may offer support or warranty protection for a fee. | ||||||
|  | 
 | ||||||
|  |   5. Conveying Modified Source Versions. | ||||||
|  | 
 | ||||||
|  |   You may convey a work based on the Program, or the modifications to | ||||||
|  | produce it from the Program, in the form of source code under the | ||||||
|  | terms of section 4, provided that you also meet all of these conditions: | ||||||
|  | 
 | ||||||
|  |     a) The work must carry prominent notices stating that you modified | ||||||
|  |     it, and giving a relevant date. | ||||||
|  | 
 | ||||||
|  |     b) The work must carry prominent notices stating that it is | ||||||
|  |     released under this License and any conditions added under section | ||||||
|  |     7.  This requirement modifies the requirement in section 4 to | ||||||
|  |     "keep intact all notices". | ||||||
|  | 
 | ||||||
|  |     c) You must license the entire work, as a whole, under this | ||||||
|  |     License to anyone who comes into possession of a copy.  This | ||||||
|  |     License will therefore apply, along with any applicable section 7 | ||||||
|  |     additional terms, to the whole of the work, and all its parts, | ||||||
|  |     regardless of how they are packaged.  This License gives no | ||||||
|  |     permission to license the work in any other way, but it does not | ||||||
|  |     invalidate such permission if you have separately received it. | ||||||
|  | 
 | ||||||
|  |     d) If the work has interactive user interfaces, each must display | ||||||
|  |     Appropriate Legal Notices; however, if the Program has interactive | ||||||
|  |     interfaces that do not display Appropriate Legal Notices, your | ||||||
|  |     work need not make them do so. | ||||||
|  | 
 | ||||||
|  |   A compilation of a covered work with other separate and independent | ||||||
|  | works, which are not by their nature extensions of the covered work, | ||||||
|  | and which are not combined with it such as to form a larger program, | ||||||
|  | in or on a volume of a storage or distribution medium, is called an | ||||||
|  | "aggregate" if the compilation and its resulting copyright are not | ||||||
|  | used to limit the access or legal rights of the compilation's users | ||||||
|  | beyond what the individual works permit.  Inclusion of a covered work | ||||||
|  | in an aggregate does not cause this License to apply to the other | ||||||
|  | parts of the aggregate. | ||||||
|  | 
 | ||||||
|  |   6. Conveying Non-Source Forms. | ||||||
|  | 
 | ||||||
|  |   You may convey a covered work in object code form under the terms | ||||||
|  | of sections 4 and 5, provided that you also convey the | ||||||
|  | machine-readable Corresponding Source under the terms of this License, | ||||||
|  | in one of these ways: | ||||||
|  | 
 | ||||||
|  |     a) Convey the object code in, or embodied in, a physical product | ||||||
|  |     (including a physical distribution medium), accompanied by the | ||||||
|  |     Corresponding Source fixed on a durable physical medium | ||||||
|  |     customarily used for software interchange. | ||||||
|  | 
 | ||||||
|  |     b) Convey the object code in, or embodied in, a physical product | ||||||
|  |     (including a physical distribution medium), accompanied by a | ||||||
|  |     written offer, valid for at least three years and valid for as | ||||||
|  |     long as you offer spare parts or customer support for that product | ||||||
|  |     model, to give anyone who possesses the object code either (1) a | ||||||
|  |     copy of the Corresponding Source for all the software in the | ||||||
|  |     product that is covered by this License, on a durable physical | ||||||
|  |     medium customarily used for software interchange, for a price no | ||||||
|  |     more than your reasonable cost of physically performing this | ||||||
|  |     conveying of source, or (2) access to copy the | ||||||
|  |     Corresponding Source from a network server at no charge. | ||||||
|  | 
 | ||||||
|  |     c) Convey individual copies of the object code with a copy of the | ||||||
|  |     written offer to provide the Corresponding Source.  This | ||||||
|  |     alternative is allowed only occasionally and noncommercially, and | ||||||
|  |     only if you received the object code with such an offer, in accord | ||||||
|  |     with subsection 6b. | ||||||
|  | 
 | ||||||
|  |     d) Convey the object code by offering access from a designated | ||||||
|  |     place (gratis or for a charge), and offer equivalent access to the | ||||||
|  |     Corresponding Source in the same way through the same place at no | ||||||
|  |     further charge.  You need not require recipients to copy the | ||||||
|  |     Corresponding Source along with the object code.  If the place to | ||||||
|  |     copy the object code is a network server, the Corresponding Source | ||||||
|  |     may be on a different server (operated by you or a third party) | ||||||
|  |     that supports equivalent copying facilities, provided you maintain | ||||||
|  |     clear directions next to the object code saying where to find the | ||||||
|  |     Corresponding Source.  Regardless of what server hosts the | ||||||
|  |     Corresponding Source, you remain obligated to ensure that it is | ||||||
|  |     available for as long as needed to satisfy these requirements. | ||||||
|  | 
 | ||||||
|  |     e) Convey the object code using peer-to-peer transmission, provided | ||||||
|  |     you inform other peers where the object code and Corresponding | ||||||
|  |     Source of the work are being offered to the general public at no | ||||||
|  |     charge under subsection 6d. | ||||||
|  | 
 | ||||||
|  |   A separable portion of the object code, whose source code is excluded | ||||||
|  | from the Corresponding Source as a System Library, need not be | ||||||
|  | included in conveying the object code work. | ||||||
|  | 
 | ||||||
|  |   A "User Product" is either (1) a "consumer product", which means any | ||||||
|  | tangible personal property which is normally used for personal, family, | ||||||
|  | or household purposes, or (2) anything designed or sold for incorporation | ||||||
|  | into a dwelling.  In determining whether a product is a consumer product, | ||||||
|  | doubtful cases shall be resolved in favor of coverage.  For a particular | ||||||
|  | product received by a particular user, "normally used" refers to a | ||||||
|  | typical or common use of that class of product, regardless of the status | ||||||
|  | of the particular user or of the way in which the particular user | ||||||
|  | actually uses, or expects or is expected to use, the product.  A product | ||||||
|  | is a consumer product regardless of whether the product has substantial | ||||||
|  | commercial, industrial or non-consumer uses, unless such uses represent | ||||||
|  | the only significant mode of use of the product. | ||||||
|  | 
 | ||||||
|  |   "Installation Information" for a User Product means any methods, | ||||||
|  | procedures, authorization keys, or other information required to install | ||||||
|  | and execute modified versions of a covered work in that User Product from | ||||||
|  | a modified version of its Corresponding Source.  The information must | ||||||
|  | suffice to ensure that the continued functioning of the modified object | ||||||
|  | code is in no case prevented or interfered with solely because | ||||||
|  | modification has been made. | ||||||
|  | 
 | ||||||
|  |   If you convey an object code work under this section in, or with, or | ||||||
|  | specifically for use in, a User Product, and the conveying occurs as | ||||||
|  | part of a transaction in which the right of possession and use of the | ||||||
|  | User Product is transferred to the recipient in perpetuity or for a | ||||||
|  | fixed term (regardless of how the transaction is characterized), the | ||||||
|  | Corresponding Source conveyed under this section must be accompanied | ||||||
|  | by the Installation Information.  But this requirement does not apply | ||||||
|  | if neither you nor any third party retains the ability to install | ||||||
|  | modified object code on the User Product (for example, the work has | ||||||
|  | been installed in ROM). | ||||||
|  | 
 | ||||||
|  |   The requirement to provide Installation Information does not include a | ||||||
|  | requirement to continue to provide support service, warranty, or updates | ||||||
|  | for a work that has been modified or installed by the recipient, or for | ||||||
|  | the User Product in which it has been modified or installed.  Access to a | ||||||
|  | network may be denied when the modification itself materially and | ||||||
|  | adversely affects the operation of the network or violates the rules and | ||||||
|  | protocols for communication across the network. | ||||||
|  | 
 | ||||||
|  |   Corresponding Source conveyed, and Installation Information provided, | ||||||
|  | in accord with this section must be in a format that is publicly | ||||||
|  | documented (and with an implementation available to the public in | ||||||
|  | source code form), and must require no special password or key for | ||||||
|  | unpacking, reading or copying. | ||||||
|  | 
 | ||||||
|  |   7. Additional Terms. | ||||||
|  | 
 | ||||||
|  |   "Additional permissions" are terms that supplement the terms of this | ||||||
|  | License by making exceptions from one or more of its conditions. | ||||||
|  | Additional permissions that are applicable to the entire Program shall | ||||||
|  | be treated as though they were included in this License, to the extent | ||||||
|  | that they are valid under applicable law.  If additional permissions | ||||||
|  | apply only to part of the Program, that part may be used separately | ||||||
|  | under those permissions, but the entire Program remains governed by | ||||||
|  | this License without regard to the additional permissions. | ||||||
|  | 
 | ||||||
|  |   When you convey a copy of a covered work, you may at your option | ||||||
|  | remove any additional permissions from that copy, or from any part of | ||||||
|  | it.  (Additional permissions may be written to require their own | ||||||
|  | removal in certain cases when you modify the work.)  You may place | ||||||
|  | additional permissions on material, added by you to a covered work, | ||||||
|  | for which you have or can give appropriate copyright permission. | ||||||
|  | 
 | ||||||
|  |   Notwithstanding any other provision of this License, for material you | ||||||
|  | add to a covered work, you may (if authorized by the copyright holders of | ||||||
|  | that material) supplement the terms of this License with terms: | ||||||
|  | 
 | ||||||
|  |     a) Disclaiming warranty or limiting liability differently from the | ||||||
|  |     terms of sections 15 and 16 of this License; or | ||||||
|  | 
 | ||||||
|  |     b) Requiring preservation of specified reasonable legal notices or | ||||||
|  |     author attributions in that material or in the Appropriate Legal | ||||||
|  |     Notices displayed by works containing it; or | ||||||
|  | 
 | ||||||
|  |     c) Prohibiting misrepresentation of the origin of that material, or | ||||||
|  |     requiring that modified versions of such material be marked in | ||||||
|  |     reasonable ways as different from the original version; or | ||||||
|  | 
 | ||||||
|  |     d) Limiting the use for publicity purposes of names of licensors or | ||||||
|  |     authors of the material; or | ||||||
|  | 
 | ||||||
|  |     e) Declining to grant rights under trademark law for use of some | ||||||
|  |     trade names, trademarks, or service marks; or | ||||||
|  | 
 | ||||||
|  |     f) Requiring indemnification of licensors and authors of that | ||||||
|  |     material by anyone who conveys the material (or modified versions of | ||||||
|  |     it) with contractual assumptions of liability to the recipient, for | ||||||
|  |     any liability that these contractual assumptions directly impose on | ||||||
|  |     those licensors and authors. | ||||||
|  | 
 | ||||||
|  |   All other non-permissive additional terms are considered "further | ||||||
|  | restrictions" within the meaning of section 10.  If the Program as you | ||||||
|  | received it, or any part of it, contains a notice stating that it is | ||||||
|  | governed by this License along with a term that is a further | ||||||
|  | restriction, you may remove that term.  If a license document contains | ||||||
|  | a further restriction but permits relicensing or conveying under this | ||||||
|  | License, you may add to a covered work material governed by the terms | ||||||
|  | of that license document, provided that the further restriction does | ||||||
|  | not survive such relicensing or conveying. | ||||||
|  | 
 | ||||||
|  |   If you add terms to a covered work in accord with this section, you | ||||||
|  | must place, in the relevant source files, a statement of the | ||||||
|  | additional terms that apply to those files, or a notice indicating | ||||||
|  | where to find the applicable terms. | ||||||
|  | 
 | ||||||
|  |   Additional terms, permissive or non-permissive, may be stated in the | ||||||
|  | form of a separately written license, or stated as exceptions; | ||||||
|  | the above requirements apply either way. | ||||||
|  | 
 | ||||||
|  |   8. Termination. | ||||||
|  | 
 | ||||||
|  |   You may not propagate or modify a covered work except as expressly | ||||||
|  | provided under this License.  Any attempt otherwise to propagate or | ||||||
|  | modify it is void, and will automatically terminate your rights under | ||||||
|  | this License (including any patent licenses granted under the third | ||||||
|  | paragraph of section 11). | ||||||
|  | 
 | ||||||
|  |   However, if you cease all violation of this License, then your | ||||||
|  | license from a particular copyright holder is reinstated (a) | ||||||
|  | provisionally, unless and until the copyright holder explicitly and | ||||||
|  | finally terminates your license, and (b) permanently, if the copyright | ||||||
|  | holder fails to notify you of the violation by some reasonable means | ||||||
|  | prior to 60 days after the cessation. | ||||||
|  | 
 | ||||||
|  |   Moreover, your license from a particular copyright holder is | ||||||
|  | reinstated permanently if the copyright holder notifies you of the | ||||||
|  | violation by some reasonable means, this is the first time you have | ||||||
|  | received notice of violation of this License (for any work) from that | ||||||
|  | copyright holder, and you cure the violation prior to 30 days after | ||||||
|  | your receipt of the notice. | ||||||
|  | 
 | ||||||
|  |   Termination of your rights under this section does not terminate the | ||||||
|  | licenses of parties who have received copies or rights from you under | ||||||
|  | this License.  If your rights have been terminated and not permanently | ||||||
|  | reinstated, you do not qualify to receive new licenses for the same | ||||||
|  | material under section 10. | ||||||
|  | 
 | ||||||
|  |   9. Acceptance Not Required for Having Copies. | ||||||
|  | 
 | ||||||
|  |   You are not required to accept this License in order to receive or | ||||||
|  | run a copy of the Program.  Ancillary propagation of a covered work | ||||||
|  | occurring solely as a consequence of using peer-to-peer transmission | ||||||
|  | to receive a copy likewise does not require acceptance.  However, | ||||||
|  | nothing other than this License grants you permission to propagate or | ||||||
|  | modify any covered work.  These actions infringe copyright if you do | ||||||
|  | not accept this License.  Therefore, by modifying or propagating a | ||||||
|  | covered work, you indicate your acceptance of this License to do so. | ||||||
|  | 
 | ||||||
|  |   10. Automatic Licensing of Downstream Recipients. | ||||||
|  | 
 | ||||||
|  |   Each time you convey a covered work, the recipient automatically | ||||||
|  | receives a license from the original licensors, to run, modify and | ||||||
|  | propagate that work, subject to this License.  You are not responsible | ||||||
|  | for enforcing compliance by third parties with this License. | ||||||
|  | 
 | ||||||
|  |   An "entity transaction" is a transaction transferring control of an | ||||||
|  | organization, or substantially all assets of one, or subdividing an | ||||||
|  | organization, or merging organizations.  If propagation of a covered | ||||||
|  | work results from an entity transaction, each party to that | ||||||
|  | transaction who receives a copy of the work also receives whatever | ||||||
|  | licenses to the work the party's predecessor in interest had or could | ||||||
|  | give under the previous paragraph, plus a right to possession of the | ||||||
|  | Corresponding Source of the work from the predecessor in interest, if | ||||||
|  | the predecessor has it or can get it with reasonable efforts. | ||||||
|  | 
 | ||||||
|  |   You may not impose any further restrictions on the exercise of the | ||||||
|  | rights granted or affirmed under this License.  For example, you may | ||||||
|  | not impose a license fee, royalty, or other charge for exercise of | ||||||
|  | rights granted under this License, and you may not initiate litigation | ||||||
|  | (including a cross-claim or counterclaim in a lawsuit) alleging that | ||||||
|  | any patent claim is infringed by making, using, selling, offering for | ||||||
|  | sale, or importing the Program or any portion of it. | ||||||
|  | 
 | ||||||
|  |   11. Patents. | ||||||
|  | 
 | ||||||
|  |   A "contributor" is a copyright holder who authorizes use under this | ||||||
|  | License of the Program or a work on which the Program is based.  The | ||||||
|  | work thus licensed is called the contributor's "contributor version". | ||||||
|  | 
 | ||||||
|  |   A contributor's "essential patent claims" are all patent claims | ||||||
|  | owned or controlled by the contributor, whether already acquired or | ||||||
|  | hereafter acquired, that would be infringed by some manner, permitted | ||||||
|  | by this License, of making, using, or selling its contributor version, | ||||||
|  | but do not include claims that would be infringed only as a | ||||||
|  | consequence of further modification of the contributor version.  For | ||||||
|  | purposes of this definition, "control" includes the right to grant | ||||||
|  | patent sublicenses in a manner consistent with the requirements of | ||||||
|  | this License. | ||||||
|  | 
 | ||||||
|  |   Each contributor grants you a non-exclusive, worldwide, royalty-free | ||||||
|  | patent license under the contributor's essential patent claims, to | ||||||
|  | make, use, sell, offer for sale, import and otherwise run, modify and | ||||||
|  | propagate the contents of its contributor version. | ||||||
|  | 
 | ||||||
|  |   In the following three paragraphs, a "patent license" is any express | ||||||
|  | agreement or commitment, however denominated, not to enforce a patent | ||||||
|  | (such as an express permission to practice a patent or covenant not to | ||||||
|  | sue for patent infringement).  To "grant" such a patent license to a | ||||||
|  | party means to make such an agreement or commitment not to enforce a | ||||||
|  | patent against the party. | ||||||
|  | 
 | ||||||
|  |   If you convey a covered work, knowingly relying on a patent license, | ||||||
|  | and the Corresponding Source of the work is not available for anyone | ||||||
|  | to copy, free of charge and under the terms of this License, through a | ||||||
|  | publicly available network server or other readily accessible means, | ||||||
|  | then you must either (1) cause the Corresponding Source to be so | ||||||
|  | available, or (2) arrange to deprive yourself of the benefit of the | ||||||
|  | patent license for this particular work, or (3) arrange, in a manner | ||||||
|  | consistent with the requirements of this License, to extend the patent | ||||||
|  | license to downstream recipients.  "Knowingly relying" means you have | ||||||
|  | actual knowledge that, but for the patent license, your conveying the | ||||||
|  | covered work in a country, or your recipient's use of the covered work | ||||||
|  | in a country, would infringe one or more identifiable patents in that | ||||||
|  | country that you have reason to believe are valid. | ||||||
|  | 
 | ||||||
|  |   If, pursuant to or in connection with a single transaction or | ||||||
|  | arrangement, you convey, or propagate by procuring conveyance of, a | ||||||
|  | covered work, and grant a patent license to some of the parties | ||||||
|  | receiving the covered work authorizing them to use, propagate, modify | ||||||
|  | or convey a specific copy of the covered work, then the patent license | ||||||
|  | you grant is automatically extended to all recipients of the covered | ||||||
|  | work and works based on it. | ||||||
|  | 
 | ||||||
|  |   A patent license is "discriminatory" if it does not include within | ||||||
|  | the scope of its coverage, prohibits the exercise of, or is | ||||||
|  | conditioned on the non-exercise of one or more of the rights that are | ||||||
|  | specifically granted under this License.  You may not convey a covered | ||||||
|  | work if you are a party to an arrangement with a third party that is | ||||||
|  | in the business of distributing software, under which you make payment | ||||||
|  | to the third party based on the extent of your activity of conveying | ||||||
|  | the work, and under which the third party grants, to any of the | ||||||
|  | parties who would receive the covered work from you, a discriminatory | ||||||
|  | patent license (a) in connection with copies of the covered work | ||||||
|  | conveyed by you (or copies made from those copies), or (b) primarily | ||||||
|  | for and in connection with specific products or compilations that | ||||||
|  | contain the covered work, unless you entered into that arrangement, | ||||||
|  | or that patent license was granted, prior to 28 March 2007. | ||||||
|  | 
 | ||||||
|  |   Nothing in this License shall be construed as excluding or limiting | ||||||
|  | any implied license or other defenses to infringement that may | ||||||
|  | otherwise be available to you under applicable patent law. | ||||||
|  | 
 | ||||||
|  |   12. No Surrender of Others' Freedom. | ||||||
|  | 
 | ||||||
|  |   If conditions are imposed on you (whether by court order, agreement or | ||||||
|  | otherwise) that contradict the conditions of this License, they do not | ||||||
|  | excuse you from the conditions of this License.  If you cannot convey a | ||||||
|  | covered work so as to satisfy simultaneously your obligations under this | ||||||
|  | License and any other pertinent obligations, then as a consequence you may | ||||||
|  | not convey it at all.  For example, if you agree to terms that obligate you | ||||||
|  | to collect a royalty for further conveying from those to whom you convey | ||||||
|  | the Program, the only way you could satisfy both those terms and this | ||||||
|  | License would be to refrain entirely from conveying the Program. | ||||||
|  | 
 | ||||||
|  |   13. Remote Network Interaction; Use with the GNU General Public License. | ||||||
|  | 
 | ||||||
|  |   Notwithstanding any other provision of this License, if you modify the | ||||||
|  | Program, your modified version must prominently offer all users | ||||||
|  | interacting with it remotely through a computer network (if your version | ||||||
|  | supports such interaction) an opportunity to receive the Corresponding | ||||||
|  | Source of your version by providing access to the Corresponding Source | ||||||
|  | from a network server at no charge, through some standard or customary | ||||||
|  | means of facilitating copying of software.  This Corresponding Source | ||||||
|  | shall include the Corresponding Source for any work covered by version 3 | ||||||
|  | of the GNU General Public License that is incorporated pursuant to the | ||||||
|  | following paragraph. | ||||||
|  | 
 | ||||||
|  |   Notwithstanding any other provision of this License, you have | ||||||
|  | permission to link or combine any covered work with a work licensed | ||||||
|  | under version 3 of the GNU General Public License into a single | ||||||
|  | combined work, and to convey the resulting work.  The terms of this | ||||||
|  | License will continue to apply to the part which is the covered work, | ||||||
|  | but the work with which it is combined will remain governed by version | ||||||
|  | 3 of the GNU General Public License. | ||||||
|  | 
 | ||||||
|  |   14. Revised Versions of this License. | ||||||
|  | 
 | ||||||
|  |   The Free Software Foundation may publish revised and/or new versions of | ||||||
|  | the GNU Affero General Public License from time to time.  Such new versions | ||||||
|  | will be similar in spirit to the present version, but may differ in detail to | ||||||
|  | address new problems or concerns. | ||||||
|  | 
 | ||||||
|  |   Each version is given a distinguishing version number.  If the | ||||||
|  | Program specifies that a certain numbered version of the GNU Affero General | ||||||
|  | Public License "or any later version" applies to it, you have the | ||||||
|  | option of following the terms and conditions either of that numbered | ||||||
|  | version or of any later version published by the Free Software | ||||||
|  | Foundation.  If the Program does not specify a version number of the | ||||||
|  | GNU Affero General Public License, you may choose any version ever published | ||||||
|  | by the Free Software Foundation. | ||||||
|  | 
 | ||||||
|  |   If the Program specifies that a proxy can decide which future | ||||||
|  | versions of the GNU Affero General Public License can be used, that proxy's | ||||||
|  | public statement of acceptance of a version permanently authorizes you | ||||||
|  | to choose that version for the Program. | ||||||
|  | 
 | ||||||
|  |   Later license versions may give you additional or different | ||||||
|  | permissions.  However, no additional obligations are imposed on any | ||||||
|  | author or copyright holder as a result of your choosing to follow a | ||||||
|  | later version. | ||||||
|  | 
 | ||||||
|  |   15. Disclaimer of Warranty. | ||||||
|  | 
 | ||||||
|  |   THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY | ||||||
|  | APPLICABLE LAW.  EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT | ||||||
|  | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY | ||||||
|  | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, | ||||||
|  | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR | ||||||
|  | PURPOSE.  THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM | ||||||
|  | IS WITH YOU.  SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF | ||||||
|  | ALL NECESSARY SERVICING, REPAIR OR CORRECTION. | ||||||
|  | 
 | ||||||
|  |   16. Limitation of Liability. | ||||||
|  | 
 | ||||||
|  |   IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING | ||||||
|  | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS | ||||||
|  | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY | ||||||
|  | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE | ||||||
|  | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF | ||||||
|  | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD | ||||||
|  | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS), | ||||||
|  | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF | ||||||
|  | SUCH DAMAGES. | ||||||
|  | 
 | ||||||
|  |   17. Interpretation of Sections 15 and 16. | ||||||
|  | 
 | ||||||
|  |   If the disclaimer of warranty and limitation of liability provided | ||||||
|  | above cannot be given local legal effect according to their terms, | ||||||
|  | reviewing courts shall apply local law that most closely approximates | ||||||
|  | an absolute waiver of all civil liability in connection with the | ||||||
|  | Program, unless a warranty or assumption of liability accompanies a | ||||||
|  | copy of the Program in return for a fee. | ||||||
|  | 
 | ||||||
|  |                      END OF TERMS AND CONDITIONS | ||||||
|  | 
 | ||||||
|  |             How to Apply These Terms to Your New Programs | ||||||
|  | 
 | ||||||
|  |   If you develop a new program, and you want it to be of the greatest | ||||||
|  | possible use to the public, the best way to achieve this is to make it | ||||||
|  | free software which everyone can redistribute and change under these terms. | ||||||
|  | 
 | ||||||
|  |   To do so, attach the following notices to the program.  It is safest | ||||||
|  | to attach them to the start of each source file to most effectively | ||||||
|  | state the exclusion of warranty; and each file should have at least | ||||||
|  | the "copyright" line and a pointer to where the full notice is found. | ||||||
|  | 
 | ||||||
|  |     <one line to give the program's name and a brief idea of what it does.> | ||||||
|  |     Copyright (C) <year>  <name of author> | ||||||
|  | 
 | ||||||
|  |     This program is free software: you can redistribute it and/or modify | ||||||
|  |     it under the terms of the GNU Affero General Public License as published by | ||||||
|  |     the Free Software Foundation, either version 3 of the License, or | ||||||
|  |     (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |     This program is distributed in the hope that it will be useful, | ||||||
|  |     but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |     GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |     You should have received a copy of the GNU Affero General Public License | ||||||
|  |     along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | 
 | ||||||
|  | Also add information on how to contact you by electronic and paper mail. | ||||||
|  | 
 | ||||||
|  |   If your software can interact with users remotely through a computer | ||||||
|  | network, you should also make sure that it provides a way for users to | ||||||
|  | get its source.  For example, if your program is a web application, its | ||||||
|  | interface could display a "Source" link that leads users to an archive | ||||||
|  | of the code.  There are many ways you could offer source, and different | ||||||
|  | solutions will be better for different programs; see section 13 for the | ||||||
|  | specific requirements. | ||||||
|  | 
 | ||||||
|  |   You should also get your employer (if you work as a programmer) or school, | ||||||
|  | if any, to sign a "copyright disclaimer" for the program, if necessary. | ||||||
|  | For more information on this, and how to apply and follow the GNU AGPL, see | ||||||
|  | <http://www.gnu.org/licenses/>. | ||||||
|  | ``` | ||||||
|  | @ -1,35 +0,0 @@ | ||||||
| @ECHO OFF |  | ||||||
| 
 |  | ||||||
| pushd %~dp0 |  | ||||||
| 
 |  | ||||||
| REM Command file for Sphinx documentation |  | ||||||
| 
 |  | ||||||
| if "%SPHINXBUILD%" == "" ( |  | ||||||
| 	set SPHINXBUILD=sphinx-build |  | ||||||
| ) |  | ||||||
| set SOURCEDIR=. |  | ||||||
| set BUILDDIR=_build |  | ||||||
| 
 |  | ||||||
| if "%1" == "" goto help |  | ||||||
| 
 |  | ||||||
| %SPHINXBUILD% >NUL 2>NUL |  | ||||||
| if errorlevel 9009 ( |  | ||||||
| 	echo. |  | ||||||
| 	echo.The 'sphinx-build' command was not found. Make sure you have Sphinx |  | ||||||
| 	echo.installed, then set the SPHINXBUILD environment variable to point |  | ||||||
| 	echo.to the full path of the 'sphinx-build' executable. Alternatively you |  | ||||||
| 	echo.may add the Sphinx directory to PATH. |  | ||||||
| 	echo. |  | ||||||
| 	echo.If you don't have Sphinx installed, grab it from |  | ||||||
| 	echo.http://sphinx-doc.org/ |  | ||||||
| 	exit /b 1 |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| %SPHINXBUILD% -M %1 %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% |  | ||||||
| goto end |  | ||||||
| 
 |  | ||||||
| :help |  | ||||||
| %SPHINXBUILD% -M help %SOURCEDIR% %BUILDDIR% %SPHINXOPTS% %O% |  | ||||||
| 
 |  | ||||||
| :end |  | ||||||
| popd |  | ||||||
|  | @ -1,42 +0,0 @@ | ||||||
| Overview |  | ||||||
| -------- |  | ||||||
| 
 |  | ||||||
| GoToSocial will solve your problem of where to start with documentation, |  | ||||||
| by providing a basic explanation of how to do it easily. |  | ||||||
| 
 |  | ||||||
| Look how easy it is to use:: |  | ||||||
| 
 |  | ||||||
|     import project |  | ||||||
|     # Get your stuff done |  | ||||||
|     project.do_stuff() |  | ||||||
| 
 |  | ||||||
| Features |  | ||||||
| -------- |  | ||||||
| 
 |  | ||||||
| - Be awesome |  | ||||||
| - Make things faster |  | ||||||
| 
 |  | ||||||
| Installation |  | ||||||
| ------------ |  | ||||||
| 
 |  | ||||||
| Install $project by running: |  | ||||||
| 
 |  | ||||||
|     install project |  | ||||||
| 
 |  | ||||||
| Open Source |  | ||||||
| ----------- |  | ||||||
| 
 |  | ||||||
| GoToSocial is a free and open-source project. |  | ||||||
| 
 |  | ||||||
| - Issue Tracker: https://github.com/gotosocial/gotosocial/issues |  | ||||||
| - Source Code: https://github.com/gotosocial/gotosocial |  | ||||||
| 
 |  | ||||||
| Support |  | ||||||
| ------- |  | ||||||
| 
 |  | ||||||
| If you are having issues, please let us know. |  | ||||||
| 
 |  | ||||||
| License |  | ||||||
| ------- |  | ||||||
| 
 |  | ||||||
| GoToSocial is licensed under the `GNU AFFERO GENERAL PUBLIC LICENSE <https://github.com/gotosocial/gotosocial/blob/main/LICENSE>`_ (AGPLv3). |  | ||||||
|  | @ -1,9 +0,0 @@ | ||||||
| About GoToSocial |  | ||||||
| ---------------- |  | ||||||
| 
 |  | ||||||
| These documents give a general overview of what GoToSocial is, and what it's trying to achieve. |  | ||||||
| 
 |  | ||||||
| .. toctree:: |  | ||||||
|     :maxdepth: 1 |  | ||||||
| 
 |  | ||||||
|     about |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| ActivityPub / Federation |  | ||||||
| ------------------------ |  | ||||||
| 
 |  | ||||||
| This document describes the API used for federating with other ActivityPub instances. |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| Client APIs |  | ||||||
| ----------- |  | ||||||
| 
 |  | ||||||
| This document describes the client APIs exposed by GoToSocial to the outside world. |  | ||||||
|  | @ -1,13 +0,0 @@ | ||||||
| API |  | ||||||
| --- |  | ||||||
| 
 |  | ||||||
| These pages describe the different APIs provided by GoToSocial, both for front-end (client) interaction, |  | ||||||
| and for federation. The intended audience for these documents is developers looking to implement a front-end |  | ||||||
| or bot for GoToSocial. |  | ||||||
| 
 |  | ||||||
| .. toctree:: |  | ||||||
|     :maxdepth: 1 |  | ||||||
| 
 |  | ||||||
|     internal |  | ||||||
|     client |  | ||||||
|     activitypub |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| Internal |  | ||||||
| -------- |  | ||||||
| 
 |  | ||||||
| This document describes the internal APIs used by GoToSocial. |  | ||||||
|  | @ -1,6 +0,0 @@ | ||||||
| Federation |  | ||||||
| ---------- |  | ||||||
| 
 |  | ||||||
| In the spirit of Mastodon and other `Fediverse <https://en.wikipedia.org/wiki/Fediverse>`_ ("federation universe") applications, |  | ||||||
| GoToSocial uses the `ActivityPub <https://www.w3.org/TR/activitypub/>`_ protocol to communicate between its own servers and those |  | ||||||
| of compliant software. |  | ||||||
|  | @ -1,10 +0,0 @@ | ||||||
| GoToSocial Design |  | ||||||
| ----------------- |  | ||||||
| 
 |  | ||||||
| These documents describe the design of GoToSocial and provide rationale for design decisions, safety features, etc. |  | ||||||
| 
 |  | ||||||
| .. toctree:: |  | ||||||
|     :maxdepth: 1 |  | ||||||
| 
 |  | ||||||
|     federation |  | ||||||
|     safety_features |  | ||||||
|  | @ -1,4 +0,0 @@ | ||||||
| Safety Features |  | ||||||
| --------------- |  | ||||||
| 
 |  | ||||||
| Blah blah blah blah. |  | ||||||
|  | @ -17,6 +17,7 @@ | ||||||
| ########################### | ########################### | ||||||
| ##### GENERAL CONFIG ###### | ##### GENERAL CONFIG ###### | ||||||
| ########################### | ########################### | ||||||
|  | 
 | ||||||
| # String. Log level to use throughout the application. Must be lower-case. | # String. Log level to use throughout the application. Must be lower-case. | ||||||
| # Options: ["trace","debug","info","warn","error","fatal"] | # Options: ["trace","debug","info","warn","error","fatal"] | ||||||
| # Default: "info" | # Default: "info" | ||||||
|  | @ -54,8 +55,10 @@ protocol: "https" | ||||||
| ############################ | ############################ | ||||||
| ##### DATABASE CONFIG ###### | ##### DATABASE CONFIG ###### | ||||||
| ############################ | ############################ | ||||||
|  | 
 | ||||||
| # Config pertaining to the Gotosocial database connection | # Config pertaining to the Gotosocial database connection | ||||||
| db: | db: | ||||||
|  | 
 | ||||||
|   # String. Database type. |   # String. Database type. | ||||||
|   # Options: ["postgres"] |   # Options: ["postgres"] | ||||||
|   # Default: "postgres" |   # Default: "postgres" | ||||||
|  | @ -105,8 +108,10 @@ db: | ||||||
| ############################### | ############################### | ||||||
| ##### WEB TEMPLATE CONFIG ##### | ##### WEB TEMPLATE CONFIG ##### | ||||||
| ############################### | ############################### | ||||||
|  | 
 | ||||||
| # Config pertaining to templating of web pages/email notifications and the like | # Config pertaining to templating of web pages/email notifications and the like | ||||||
| template: | template: | ||||||
|  | 
 | ||||||
|   # String. Directory from which gotosocial will attempt to load html templates (.tmpl files). |   # String. Directory from which gotosocial will attempt to load html templates (.tmpl files). | ||||||
|   # Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"] |   # Examples: ["/some/absolute/path/", "./relative/path/", "../../some/weird/path/"] | ||||||
|   # Default: "./web/template/" |   # Default: "./web/template/" | ||||||
|  | @ -120,8 +125,10 @@ template: | ||||||
| ########################### | ########################### | ||||||
| ##### ACCOUNTS CONFIG ##### | ##### ACCOUNTS CONFIG ##### | ||||||
| ########################### | ########################### | ||||||
|  | 
 | ||||||
| # Config pertaining to creation and maintenance of accounts on the server, as well as defaults for new accounts. | # Config pertaining to creation and maintenance of accounts on the server, as well as defaults for new accounts. | ||||||
| accounts: | accounts: | ||||||
|  | 
 | ||||||
|   # Bool. Do we want people to be able to just submit sign up requests, or do we want invite only? |   # Bool. Do we want people to be able to just submit sign up requests, or do we want invite only? | ||||||
|   # Options: [true, false] |   # Options: [true, false] | ||||||
|   # Default: true |   # Default: true | ||||||
|  | @ -140,8 +147,10 @@ accounts: | ||||||
| ######################## | ######################## | ||||||
| ##### MEDIA CONFIG ##### | ##### MEDIA CONFIG ##### | ||||||
| ######################## | ######################## | ||||||
|  | 
 | ||||||
| # Config pertaining to user media uploads (videos, image, image descriptions). | # Config pertaining to user media uploads (videos, image, image descriptions). | ||||||
| media: | media: | ||||||
|  | 
 | ||||||
|   # Int. Maximum allowed image upload size in bytes. |   # Int. Maximum allowed image upload size in bytes. | ||||||
|   # Examples: [2097152, 10485760] |   # Examples: [2097152, 10485760] | ||||||
|   # Default: 2097152 -- aka 2MB |   # Default: 2097152 -- aka 2MB | ||||||
|  | @ -165,8 +174,10 @@ media: | ||||||
| ########################## | ########################## | ||||||
| ##### STORAGE CONFIG ##### | ##### STORAGE CONFIG ##### | ||||||
| ########################## | ########################## | ||||||
|  | 
 | ||||||
| # Config pertaining to storage of user-created uploads (videos, images, etc). | # Config pertaining to storage of user-created uploads (videos, images, etc). | ||||||
| storage: | storage: | ||||||
|  | 
 | ||||||
|   # String. Type of storage backend to use. |   # String. Type of storage backend to use. | ||||||
|   # Examples: ["local", "s3"] |   # Examples: ["local", "s3"] | ||||||
|   # Default: "local" (storage on local disk) |   # Default: "local" (storage on local disk) | ||||||
|  | @ -203,8 +214,10 @@ storage: | ||||||
| ########################### | ########################### | ||||||
| ##### STATUSES CONFIG ##### | ##### STATUSES CONFIG ##### | ||||||
| ########################### | ########################### | ||||||
|  | 
 | ||||||
| # Config pertaining to the creation of statuses/posts, and permitted limits. | # Config pertaining to the creation of statuses/posts, and permitted limits. | ||||||
| statuses: | statuses: | ||||||
|  | 
 | ||||||
|   # Int. Maximum amount of characters permitted for a new status. |   # Int. Maximum amount of characters permitted for a new status. | ||||||
|   # Note that going way higher than the default might break federation. |   # Note that going way higher than the default might break federation. | ||||||
|   # Examples: [140, 500, 5000] |   # Examples: [140, 500, 5000] | ||||||
|  | @ -238,8 +251,10 @@ statuses: | ||||||
| ############################## | ############################## | ||||||
| ##### LETSENCRYPT CONFIG ##### | ##### LETSENCRYPT CONFIG ##### | ||||||
| ############################## | ############################## | ||||||
|  | 
 | ||||||
| # Config pertaining to the automatic acquisition and use of LetsEncrypt HTTPS certificates. | # Config pertaining to the automatic acquisition and use of LetsEncrypt HTTPS certificates. | ||||||
| letsEncrypt: | letsEncrypt: | ||||||
|  | 
 | ||||||
|   # Bool. Whether or not letsencrypt should be enabled for the server. |   # Bool. Whether or not letsencrypt should be enabled for the server. | ||||||
|   # If true, the server will serve on port 443 (https) and obtain letsencrypt |   # If true, the server will serve on port 443 (https) and obtain letsencrypt | ||||||
|   # certificates automatically. |   # certificates automatically. | ||||||
|  | @ -265,3 +280,58 @@ letsEncrypt: | ||||||
|   # Examples: ["admin@example.org"] |   # Examples: ["admin@example.org"] | ||||||
|   # Default: "" |   # Default: "" | ||||||
|   emailAddress: "" |   emailAddress: "" | ||||||
|  | 
 | ||||||
|  | ####################### | ||||||
|  | ##### OIDC CONFIG ##### | ||||||
|  | ####################### | ||||||
|  | 
 | ||||||
|  | # Config for authentication with an external OIDC provider (Dex, Google, Auth0, etc). | ||||||
|  | oidc: | ||||||
|  | 
 | ||||||
|  |   # Bool. Enable authentication with external OIDC provider. If set to true, then | ||||||
|  |   # the other OIDC options must be set as well. If this is set to false, then the standard | ||||||
|  |   # internal oauth flow will be used, where users sign in to GtS with username/password. | ||||||
|  |   # Options: [true, false] | ||||||
|  |   # Default: false | ||||||
|  |   enabled: false | ||||||
|  | 
 | ||||||
|  |   # String. Name of the oidc idp (identity provider). This will be shown to users when | ||||||
|  |   # they log in. | ||||||
|  |   # Examples: ["Google", "Dex", "Auth0"] | ||||||
|  |   # Default: "" | ||||||
|  |   idpName: "" | ||||||
|  | 
 | ||||||
|  |   # Bool. Skip the normal verification flow of tokens returned from the OIDC provider, ie., | ||||||
|  |   # don't check the expiry or signature. This should only be used in debugging or testing, | ||||||
|  |   # never ever in a production environment as it's extremely unsafe! | ||||||
|  |   # Options: [true, false] | ||||||
|  |   # Default: false | ||||||
|  |   skipVerification: false | ||||||
|  | 
 | ||||||
|  |   # String. The OIDC issuer URI. This is where GtS will redirect users to for login. | ||||||
|  |   # Typically this will look like a standard web URL. | ||||||
|  |   # Examples: ["https://auth.example.org", "https://example.org/auth"] | ||||||
|  |   # Default: "" | ||||||
|  |   issuer: "" | ||||||
|  | 
 | ||||||
|  |   # String. The ID for this client as registered with the OIDC provider. | ||||||
|  |   # Examples: ["some-client-id", "fda3772a-ad35-41c9-9a59-f1943ad18f54"] | ||||||
|  |   # Default: "" | ||||||
|  |   clientID: "" | ||||||
|  | 
 | ||||||
|  |   # String. The secret for this client as registered with the OIDC provider. | ||||||
|  |   # Examples: ["super-secret-business", "79379cf5-8057-426d-bb83-af504d98a7b0"] | ||||||
|  |   # Default: "" | ||||||
|  |   clientSecret: "" | ||||||
|  | 
 | ||||||
|  |   # Array of string. Scopes to request from the OIDC provider. The returned values will be used to | ||||||
|  |   # populate users created in GtS as a result of the authentication flow. 'openid' and 'email' are required. | ||||||
|  |   # 'profile' is used to extract a username for the newly created user. | ||||||
|  |   # 'groups' is optional and can be used to determine if a user is an admin (if they're in the group 'admin' or 'admins'). | ||||||
|  |   # Examples: See eg., https://auth0.com/docs/scopes/openid-connect-scopes | ||||||
|  |   # Default: ["openid", "email", "profile", "groups"] | ||||||
|  |   scopes: | ||||||
|  |     - "openid" | ||||||
|  |     - "email" | ||||||
|  |     - "profile" | ||||||
|  |     - "groups" | ||||||
|  |  | ||||||
							
								
								
									
										2
									
								
								go.mod
									
										
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
										
									
									
									
								
							|  | @ -4,6 +4,7 @@ go 1.16 | ||||||
| 
 | 
 | ||||||
| require ( | require ( | ||||||
| 	github.com/buckket/go-blurhash v1.1.0 | 	github.com/buckket/go-blurhash v1.1.0 | ||||||
|  | 	github.com/coreos/go-oidc/v3 v3.0.0 | ||||||
| 	github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect | 	github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect | ||||||
| 	github.com/dsoprea/go-exif v0.0.0-20210512055020-8213cfabc61b // indirect | 	github.com/dsoprea/go-exif v0.0.0-20210512055020-8213cfabc61b // indirect | ||||||
| 	github.com/dsoprea/go-exif/v2 v2.0.0-20210512055020-8213cfabc61b // indirect | 	github.com/dsoprea/go-exif/v2 v2.0.0-20210512055020-8213cfabc61b // indirect | ||||||
|  | @ -50,6 +51,7 @@ require ( | ||||||
| 	github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect | 	github.com/vmihailenco/msgpack/v5 v5.3.4 // indirect | ||||||
| 	github.com/wagslane/go-password-validator v0.3.0 | 	github.com/wagslane/go-password-validator v0.3.0 | ||||||
| 	golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a | 	golang.org/x/crypto v0.0.0-20210513164829-c07d793c2f9a | ||||||
|  | 	golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d | ||||||
| 	golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea // indirect | 	golang.org/x/sys v0.0.0-20210525143221-35b2ab0089ea // indirect | ||||||
| 	golang.org/x/text v0.3.6 | 	golang.org/x/text v0.3.6 | ||||||
| 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect | 	gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c // indirect | ||||||
|  |  | ||||||
							
								
								
									
										11
									
								
								go.sum
									
										
									
									
									
								
							
							
						
						
									
										11
									
								
								go.sum
									
										
									
									
									
								
							|  | @ -1,4 +1,5 @@ | ||||||
| cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||||
|  | cloud.google.com/go v0.34.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= | ||||||
| github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= | ||||||
| github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= | github.com/ajg/form v1.5.1 h1:t9c7v8JUKu/XxOGBU0yjNpaMloxGEJhUkqFRq0ibGeU= | ||||||
| github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= | github.com/ajg/form v1.5.1/go.mod h1:uL1WgH+h2mgNtvBq0339dVnzXdBETtL2LeUXaIv25UY= | ||||||
|  | @ -14,6 +15,8 @@ github.com/buckket/go-blurhash v1.1.0 h1:X5M6r0LIvwdvKiUtiNcRL2YlmOfMzYobI3VCKCZ | ||||||
| github.com/buckket/go-blurhash v1.1.0/go.mod h1:aT2iqo5W9vu9GpyoLErKfTHwgODsZp3bQfXjXJUxNb8= | github.com/buckket/go-blurhash v1.1.0/go.mod h1:aT2iqo5W9vu9GpyoLErKfTHwgODsZp3bQfXjXJUxNb8= | ||||||
| github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | github.com/census-instrumentation/opencensus-proto v0.2.1/go.mod h1:f6KPmirojxKA12rnyqOA5BBL4O983OfeGPqjHWSTneU= | ||||||
| github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= | github.com/client9/misspell v0.3.4/go.mod h1:qj6jICC3Q7zFZvVWo7KLAzC3yx5G7kyvSDkc90ppPyw= | ||||||
|  | github.com/coreos/go-oidc/v3 v3.0.0 h1:/mAA0XMgYJw2Uqm7WKGCsKnjitE/+A0FFbOmiRJm7LQ= | ||||||
|  | github.com/coreos/go-oidc/v3 v3.0.0/go.mod h1:rEJ/idjfUyfkBit1eI1fvyr+64/g9dcKpAm8MJMesvo= | ||||||
| github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= | github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= | ||||||
| github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= | github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM= | ||||||
| github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= | github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU= | ||||||
|  | @ -352,6 +355,7 @@ golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= | ||||||
| golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
| golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
| golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
|  | golang.org/x/net v0.0.0-20190108225652-1e06a53dbb7e/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
| golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | golang.org/x/net v0.0.0-20190213061140-3a22650c66bd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= | ||||||
| golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||||
| golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= | ||||||
|  | @ -362,6 +366,7 @@ golang.org/x/net v0.0.0-20200301022130-244492dfa37a/go.mod h1:z5CRVTTTmAJ677TzLL | ||||||
| golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= | ||||||
| golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | golang.org/x/net v0.0.0-20200324143707-d3edc9973b7e/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||||
| golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | golang.org/x/net v0.0.0-20200501053045-e0ff5e5a1de5/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||||
|  | golang.org/x/net v0.0.0-20200505041828-1ed23360d12c/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||||
| golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | golang.org/x/net v0.0.0-20200513185701-a91f0712d120/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||||
| golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | golang.org/x/net v0.0.0-20200520004742-59133d7f0dd7/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||||
| golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | golang.org/x/net v0.0.0-20200520182314-0ba52f642ac2/go.mod h1:qpuaurCH72eLCgpAm/N6yyVIVM9cpaDIP3A8BGJEC5A= | ||||||
|  | @ -374,8 +379,11 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT | ||||||
| golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= | golang.org/x/net v0.0.0-20210614182718-04defd469f4e h1:XpT3nA5TvE525Ne3hInMh6+GETgn27Zfm9dxsThnX2Q= | ||||||
| golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | golang.org/x/net v0.0.0-20210614182718-04defd469f4e/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y= | ||||||
| golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | golang.org/x/oauth2 v0.0.0-20180821212333-d2e6202438be/go.mod h1:N/0e6XlmueqKjAGxoOufVs8QHGRruUQn6yWY3a++T0U= | ||||||
|  | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d h1:TzXSXBo42m9gQenoE3b9BGiEpg5IG2JkU5FkPIawgtw= | ||||||
|  | golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d/go.mod h1:gOpvHmFTYa4IltrdGE7lF6nIHvwfUNPOp7c8zoXwtLw= | ||||||
| golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20180314180146-1d60e4601c6f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
|  | golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | golang.org/x/sys v0.0.0-20180525142821-c11f84a56e43/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||||
|  | @ -424,6 +432,7 @@ golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8T | ||||||
| google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | google.golang.org/appengine v1.1.0/go.mod h1:EbEs0AVv82hx2wNQdGPgUI5lhzA/G0D9YwlJXL52JkM= | ||||||
| google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | google.golang.org/appengine v1.4.0/go.mod h1:xpcJRLb0r/rnEns0DIKYYv+WjYCduHsrkT7/EB5XEv4= | ||||||
| google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | google.golang.org/appengine v1.6.5/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||||
|  | google.golang.org/appengine v1.6.7 h1:FZR1q0exgwxzPzp/aF+VccGrSfxfPpkBqjIIEq3ru6c= | ||||||
| google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | google.golang.org/appengine v1.6.7/go.mod h1:8WjMMxjGQR8xUklV/ARdw2HLXBOI7O7uCIDZVag1xfc= | ||||||
| google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | google.golang.org/genproto v0.0.0-20180817151627-c66870c02cf8/go.mod h1:JiN7NxoALGmiZfu7CAH4rXhgtRTLTxftemlI0sWmxmc= | ||||||
| google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | google.golang.org/genproto v0.0.0-20190819201941-24fa4b261c55/go.mod h1:DMBHOl98Agz4BDEuKkezgsaosCRResVns1a3J2ZsMNc= | ||||||
|  | @ -452,6 +461,8 @@ gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EV | ||||||
| gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= | ||||||
| gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= | gopkg.in/go-playground/assert.v1 v1.2.1/go.mod h1:9RXL0bg/zibRAgZUYszZSwO/z8Y/a8bDuhia5mkpMnE= | ||||||
| gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= | gopkg.in/go-playground/validator.v9 v9.29.1/go.mod h1:+c9/zcJMFNgbLvly1L1V+PpxWdVbfP1avr/N00E2vyQ= | ||||||
|  | gopkg.in/square/go-jose.v2 v2.5.1 h1:7odma5RETjNHWJnR32wx8t+Io4djHE1PqxCFx3iiZ2w= | ||||||
|  | gopkg.in/square/go-jose.v2 v2.5.1/go.mod h1:M9dMgbHiYLoDGQrXy7OpJDJWiKiU//h+vD76mk0e1AI= | ||||||
| gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7 h1:uRGJdciOHaEIrze2W8Q3AKkepLTh2hOroT7a+7czfdQ= | ||||||
| gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= | ||||||
| gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= | ||||||
|  |  | ||||||
|  | @ -26,6 +26,7 @@ import ( | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/oidc" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/router" | 	"github.com/superseriousbusiness/gotosocial/internal/router" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | @ -36,40 +37,37 @@ const ( | ||||||
| 	OauthTokenPath = "/oauth/token" | 	OauthTokenPath = "/oauth/token" | ||||||
| 	// OauthAuthorizePath is the API path for authorization requests (eg., authorize this app to act on my behalf as a user) | 	// OauthAuthorizePath is the API path for authorization requests (eg., authorize this app to act on my behalf as a user) | ||||||
| 	OauthAuthorizePath = "/oauth/authorize" | 	OauthAuthorizePath = "/oauth/authorize" | ||||||
|  | 	// CallbackPath is the API path for receiving callback tokens from external OIDC providers | ||||||
|  | 	CallbackPath = oidc.CallbackPath | ||||||
|  | 
 | ||||||
|  | 	callbackStateParam = "state" | ||||||
|  | 	callbackCodeParam  = "code" | ||||||
| 
 | 
 | ||||||
| 	sessionUserID       = "userid" | 	sessionUserID       = "userid" | ||||||
| 	sessionClientID     = "client_id" | 	sessionClientID     = "client_id" | ||||||
| 	sessionRedirectURI  = "redirect_uri" | 	sessionRedirectURI  = "redirect_uri" | ||||||
| 	sessionForceLogin   = "force_login" | 	sessionForceLogin   = "force_login" | ||||||
| 	sessionResponseType = "response_type" | 	sessionResponseType = "response_type" | ||||||
| 	sessionCode         = "code" |  | ||||||
| 	sessionScope        = "scope" | 	sessionScope        = "scope" | ||||||
|  | 	sessionState        = "state" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| var sessionKeys []string = []string{ |  | ||||||
| 	sessionUserID, |  | ||||||
| 	sessionClientID, |  | ||||||
| 	sessionRedirectURI, |  | ||||||
| 	sessionForceLogin, |  | ||||||
| 	sessionResponseType, |  | ||||||
| 	sessionCode, |  | ||||||
| 	sessionScope, |  | ||||||
| } |  | ||||||
| 
 |  | ||||||
| // Module implements the ClientAPIModule interface for | // Module implements the ClientAPIModule interface for | ||||||
| type Module struct { | type Module struct { | ||||||
| 	config *config.Config | 	config *config.Config | ||||||
| 	db     db.DB | 	db     db.DB | ||||||
| 	server oauth.Server | 	server oauth.Server | ||||||
|  | 	idp    oidc.IDP | ||||||
| 	log    *logrus.Logger | 	log    *logrus.Logger | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // New returns a new auth module | // New returns a new auth module | ||||||
| func New(config *config.Config, db db.DB, server oauth.Server, log *logrus.Logger) api.ClientModule { | func New(config *config.Config, db db.DB, server oauth.Server, idp oidc.IDP, log *logrus.Logger) api.ClientModule { | ||||||
| 	return &Module{ | 	return &Module{ | ||||||
| 		config: config, | 		config: config, | ||||||
| 		db:     db, | 		db:     db, | ||||||
| 		server: server, | 		server: server, | ||||||
|  | 		idp:    idp, | ||||||
| 		log:    log, | 		log:    log, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -84,6 +82,8 @@ func (m *Module) Route(s router.Router) error { | ||||||
| 	s.AttachHandler(http.MethodGet, OauthAuthorizePath, m.AuthorizeGETHandler) | 	s.AttachHandler(http.MethodGet, OauthAuthorizePath, m.AuthorizeGETHandler) | ||||||
| 	s.AttachHandler(http.MethodPost, OauthAuthorizePath, m.AuthorizePOSTHandler) | 	s.AttachHandler(http.MethodPost, OauthAuthorizePath, m.AuthorizePOSTHandler) | ||||||
| 
 | 
 | ||||||
|  | 	s.AttachHandler(http.MethodGet, CallbackPath, m.CallbackGETHandler) | ||||||
|  | 
 | ||||||
| 	s.AttachMiddleware(m.OauthTokenMiddleware) | 	s.AttachMiddleware(m.OauthTokenMiddleware) | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -23,9 +23,11 @@ import ( | ||||||
| 	"fmt" | 	"fmt" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/url" | 	"net/url" | ||||||
|  | 	"strings" | ||||||
| 
 | 
 | ||||||
| 	"github.com/gin-contrib/sessions" | 	"github.com/gin-contrib/sessions" | ||||||
| 	"github.com/gin-gonic/gin" | 	"github.com/gin-gonic/gin" | ||||||
|  | 	"github.com/google/uuid" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/api/model" | 	"github.com/superseriousbusiness/gotosocial/internal/api/model" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/db" | 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||||
|  | @ -46,16 +48,19 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) { | ||||||
| 		form := &model.OAuthAuthorize{} | 		form := &model.OAuthAuthorize{} | ||||||
| 		if err := c.Bind(form); err != nil { | 		if err := c.Bind(form); err != nil { | ||||||
| 			l.Debugf("invalid auth form: %s", err) | 			l.Debugf("invalid auth form: %s", err) | ||||||
| 			return | 			m.clearSession(s) | ||||||
| 		} |  | ||||||
| 		l.Tracef("parsed auth form: %+v", form) |  | ||||||
| 
 |  | ||||||
| 		if err := extractAuthForm(s, form); err != nil { |  | ||||||
| 			l.Debugf(fmt.Sprintf("error parsing form at /oauth/authorize: %s", err)) |  | ||||||
| 			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | 			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 		c.Redirect(http.StatusFound, AuthSignInPath) | 		l.Debugf("parsed auth form: %+v", form) | ||||||
|  | 
 | ||||||
|  | 		if err := extractAuthForm(s, form); err != nil { | ||||||
|  | 			l.Debugf(fmt.Sprintf("error parsing form at /oauth/authorize: %s", err)) | ||||||
|  | 			m.clearSession(s) | ||||||
|  | 			c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 		c.Redirect(http.StatusSeeOther, AuthSignInPath) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -69,6 +74,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) { | ||||||
| 		ClientID: clientID, | 		ClientID: clientID, | ||||||
| 	} | 	} | ||||||
| 	if err := m.db.GetWhere([]db.Where{{Key: sessionClientID, Value: app.ClientID}}, app); err != nil { | 	if err := m.db.GetWhere([]db.Where{{Key: sessionClientID, Value: app.ClientID}}, app); err != nil { | ||||||
|  | 		m.clearSession(s) | ||||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("no application found for client id %s", clientID)}) | 		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("no application found for client id %s", clientID)}) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | @ -78,6 +84,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) { | ||||||
| 		ID: userID, | 		ID: userID, | ||||||
| 	} | 	} | ||||||
| 	if err := m.db.GetByID(user.ID, user); err != nil { | 	if err := m.db.GetByID(user.ID, user); err != nil { | ||||||
|  | 		m.clearSession(s) | ||||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | @ -87,6 +94,7 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err := m.db.GetByID(acct.ID, acct); err != nil { | 	if err := m.db.GetByID(acct.ID, acct); err != nil { | ||||||
|  | 		m.clearSession(s) | ||||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | @ -94,11 +102,13 @@ func (m *Module) AuthorizeGETHandler(c *gin.Context) { | ||||||
| 	// Finally we should also get the redirect and scope of this particular request, as stored in the session. | 	// Finally we should also get the redirect and scope of this particular request, as stored in the session. | ||||||
| 	redirect, ok := s.Get(sessionRedirectURI).(string) | 	redirect, ok := s.Get(sessionRedirectURI).(string) | ||||||
| 	if !ok || redirect == "" { | 	if !ok || redirect == "" { | ||||||
|  | 		m.clearSession(s) | ||||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": "no redirect_uri found in session"}) | 		c.JSON(http.StatusInternalServerError, gin.H{"error": "no redirect_uri found in session"}) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	scope, ok := s.Get(sessionScope).(string) | 	scope, ok := s.Get(sessionScope).(string) | ||||||
| 	if !ok || scope == "" { | 	if !ok || scope == "" { | ||||||
|  | 		m.clearSession(s) | ||||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": "no scope found in session"}) | 		c.JSON(http.StatusInternalServerError, gin.H{"error": "no scope found in session"}) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
|  | @ -128,48 +138,42 @@ func (m *Module) AuthorizePOSTHandler(c *gin.Context) { | ||||||
| 	// recreate it on the request so that it can be used further by the oauth2 library. | 	// recreate it on the request so that it can be used further by the oauth2 library. | ||||||
| 	// So first fetch all the values from the session. | 	// So first fetch all the values from the session. | ||||||
| 
 | 
 | ||||||
|  | 	errs := []string{} | ||||||
|  | 
 | ||||||
| 	forceLogin, ok := s.Get(sessionForceLogin).(string) | 	forceLogin, ok := s.Get(sessionForceLogin).(string) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": "session missing force_login"}) | 		forceLogin = "false" | ||||||
| 		return |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	responseType, ok := s.Get(sessionResponseType).(string) | 	responseType, ok := s.Get(sessionResponseType).(string) | ||||||
| 	if !ok || responseType == "" { | 	if !ok || responseType == "" { | ||||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": "session missing response_type"}) | 		errs = append(errs, "session missing response_type") | ||||||
| 		return |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	clientID, ok := s.Get(sessionClientID).(string) | 	clientID, ok := s.Get(sessionClientID).(string) | ||||||
| 	if !ok || clientID == "" { | 	if !ok || clientID == "" { | ||||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": "session missing client_id"}) | 		errs = append(errs, "session missing client_id") | ||||||
| 		return |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	redirectURI, ok := s.Get(sessionRedirectURI).(string) | 	redirectURI, ok := s.Get(sessionRedirectURI).(string) | ||||||
| 	if !ok || redirectURI == "" { | 	if !ok || redirectURI == "" { | ||||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": "session missing redirect_uri"}) | 		errs = append(errs, "session missing redirect_uri") | ||||||
| 		return |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	scope, ok := s.Get(sessionScope).(string) | 	scope, ok := s.Get(sessionScope).(string) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": "session missing scope"}) | 		errs = append(errs, "session missing scope") | ||||||
| 		return |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	userID, ok := s.Get(sessionUserID).(string) | 	userID, ok := s.Get(sessionUserID).(string) | ||||||
| 	if !ok { | 	if !ok { | ||||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": "session missing userid"}) | 		errs = append(errs, "session missing userid") | ||||||
| 		return |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	// we're done with the session so we can clear it now | 	m.clearSession(s) | ||||||
| 	for _, key := range sessionKeys { | 
 | ||||||
| 		s.Delete(key) | 	if len(errs) != 0 { | ||||||
| 	} | 		c.JSON(http.StatusBadRequest, gin.H{"error": strings.Join(errs, ": ")}) | ||||||
| 	if err := s.Save(); err != nil { |  | ||||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) |  | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | @ -209,5 +213,6 @@ func extractAuthForm(s sessions.Session, form *model.OAuthAuthorize) error { | ||||||
| 	s.Set(sessionClientID, form.ClientID) | 	s.Set(sessionClientID, form.ClientID) | ||||||
| 	s.Set(sessionRedirectURI, form.RedirectURI) | 	s.Set(sessionRedirectURI, form.RedirectURI) | ||||||
| 	s.Set(sessionScope, form.Scope) | 	s.Set(sessionScope, form.Scope) | ||||||
|  | 	s.Set(sessionState, uuid.NewString()) | ||||||
| 	return s.Save() | 	return s.Save() | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										219
									
								
								internal/api/client/auth/callback.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										219
									
								
								internal/api/client/auth/callback.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,219 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package auth | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | 	"net" | ||||||
|  | 	"net/http" | ||||||
|  | 	"strconv" | ||||||
|  | 	"strings" | ||||||
|  | 
 | ||||||
|  | 	"github.com/gin-contrib/sessions" | ||||||
|  | 	"github.com/gin-gonic/gin" | ||||||
|  | 	"github.com/google/uuid" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/db" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/oidc" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/util" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // CallbackGETHandler parses a token from an external auth provider. | ||||||
|  | func (m *Module) CallbackGETHandler(c *gin.Context) { | ||||||
|  | 	s := sessions.Default(c) | ||||||
|  | 
 | ||||||
|  | 	// first make sure the state set in the cookie is the same as the state returned from the external provider | ||||||
|  | 	state := c.Query(callbackStateParam) | ||||||
|  | 	if state == "" { | ||||||
|  | 		m.clearSession(s) | ||||||
|  | 		c.JSON(http.StatusForbidden, gin.H{"error": "state query not found on callback"}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	savedStateI := s.Get(sessionState) | ||||||
|  | 	savedState, ok := savedStateI.(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		m.clearSession(s) | ||||||
|  | 		c.JSON(http.StatusForbidden, gin.H{"error": "state not found in session"}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if state != savedState { | ||||||
|  | 		m.clearSession(s) | ||||||
|  | 		c.JSON(http.StatusForbidden, gin.H{"error": "state mismatch"}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	code := c.Query(callbackCodeParam) | ||||||
|  | 
 | ||||||
|  | 	claims, err := m.idp.HandleCallback(c.Request.Context(), code) | ||||||
|  | 	if err != nil { | ||||||
|  | 		m.clearSession(s) | ||||||
|  | 		c.JSON(http.StatusForbidden, gin.H{"error": err.Error()}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// We can use the client_id on the session to retrieve info about the app associated with the client_id | ||||||
|  | 	clientID, ok := s.Get(sessionClientID).(string) | ||||||
|  | 	if !ok || clientID == "" { | ||||||
|  | 		m.clearSession(s) | ||||||
|  | 		c.JSON(http.StatusInternalServerError, gin.H{"error": "no client_id found in session during callback"}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	app := >smodel.Application{ | ||||||
|  | 		ClientID: clientID, | ||||||
|  | 	} | ||||||
|  | 	if err := m.db.GetWhere([]db.Where{{Key: sessionClientID, Value: app.ClientID}}, app); err != nil { | ||||||
|  | 		m.clearSession(s) | ||||||
|  | 		c.JSON(http.StatusInternalServerError, gin.H{"error": fmt.Sprintf("no application found for client id %s", clientID)}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	user, err := m.parseUserFromClaims(claims, net.IP(c.ClientIP()), app.ID) | ||||||
|  | 	if err != nil { | ||||||
|  | 		m.clearSession(s) | ||||||
|  | 		c.JSON(http.StatusForbidden, gin.H{"error": err.Error()}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	s.Set(sessionUserID, user.ID) | ||||||
|  | 	if err := s.Save(); err != nil { | ||||||
|  | 		m.clearSession(s) | ||||||
|  | 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	c.Redirect(http.StatusFound, OauthAuthorizePath) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (m *Module) parseUserFromClaims(claims *oidc.Claims, ip net.IP, appID string) (*gtsmodel.User, error) { | ||||||
|  | 	if claims.Email == "" { | ||||||
|  | 		return nil, errors.New("no email returned in claims") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// see if we already have a user for this email address | ||||||
|  | 	user := >smodel.User{} | ||||||
|  | 	err := m.db.GetWhere([]db.Where{{Key: "email", Value: claims.Email}}, user) | ||||||
|  | 	if err == nil { | ||||||
|  | 		// we do! so we can just return it | ||||||
|  | 		return user, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if _, ok := err.(db.ErrNoEntries); !ok { | ||||||
|  | 		// we have an actual error in the database | ||||||
|  | 		return nil, fmt.Errorf("error checking database for email %s: %s", claims.Email, err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// maybe we have an unconfirmed user | ||||||
|  | 	err = m.db.GetWhere([]db.Where{{Key: "unconfirmed_email", Value: claims.Email}}, user) | ||||||
|  | 	if err == nil { | ||||||
|  | 		// user is unconfirmed so return an error | ||||||
|  | 		return nil, fmt.Errorf("user with email address %s is unconfirmed", claims.Email) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if _, ok := err.(db.ErrNoEntries); !ok { | ||||||
|  | 		// we have an actual error in the database | ||||||
|  | 		return nil, fmt.Errorf("error checking database for email %s: %s", claims.Email, err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// we don't have a confirmed or unconfirmed user with the claimed email address | ||||||
|  | 	// however, because we trust the OIDC provider, we should now create a user + account with the provided claims | ||||||
|  | 
 | ||||||
|  | 	// check if the email address is available for use; if it's not there's nothing we can so | ||||||
|  | 	if err := m.db.IsEmailAvailable(claims.Email); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("email %s not available: %s", claims.Email, err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// now we need a username | ||||||
|  | 	var username string | ||||||
|  | 
 | ||||||
|  | 	// make sure claims.Name is defined since we'll be using that for the username | ||||||
|  | 	if claims.Name == "" { | ||||||
|  | 		return nil, errors.New("no name returned in claims") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// check if we can just use claims.Name as-is | ||||||
|  | 	err = util.ValidateUsername(claims.Name) | ||||||
|  | 	if err == nil { | ||||||
|  | 		// the name we have on the claims is already a valid username | ||||||
|  | 		username = claims.Name | ||||||
|  | 	} else { | ||||||
|  | 		// not a valid username so we have to fiddle with it to try to make it valid | ||||||
|  | 		// first trim leading and trailing whitespace | ||||||
|  | 		trimmed := strings.TrimSpace(claims.Name) | ||||||
|  | 		// underscore any spaces in the middle of the name | ||||||
|  | 		underscored := strings.ReplaceAll(trimmed, " ", "_") | ||||||
|  | 		// lowercase the whole thing | ||||||
|  | 		lower := strings.ToLower(underscored) | ||||||
|  | 		// see if this is valid.... | ||||||
|  | 		if err := util.ValidateUsername(lower); err == nil { | ||||||
|  | 			// we managed to get a valid username | ||||||
|  | 			username = lower | ||||||
|  | 		} else { | ||||||
|  | 			return nil, fmt.Errorf("couldn't parse a valid username from claims.Name value of %s", claims.Name) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	var iString string | ||||||
|  | 	var found bool | ||||||
|  | 	// if the username isn't available we need to iterate on it until we find one that is | ||||||
|  | 	// we should try to do this in a predictable way so we just keep iterating i by one and trying | ||||||
|  | 	// the username with that number on the end | ||||||
|  | 	// | ||||||
|  | 	// note that for the first iteration, iString is still "" when the check is made, so our first choice | ||||||
|  | 	// is still the raw username with no integer stuck on the end | ||||||
|  | 	for i := 1; !found; i = i + 1 { | ||||||
|  | 		if err := m.db.IsUsernameAvailable(username + iString); err != nil { | ||||||
|  | 			if strings.Contains(err.Error(), "db error") { | ||||||
|  | 				// if there's an actual db error we should return | ||||||
|  | 				return nil, fmt.Errorf("error checking username availability: %s", err) | ||||||
|  | 			} | ||||||
|  | 		} else { | ||||||
|  | 			// no error so we've found a username that works | ||||||
|  | 			found = true | ||||||
|  | 			username = username + iString | ||||||
|  | 			continue | ||||||
|  | 		} | ||||||
|  | 		iString = strconv.Itoa(i) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// check if the user is in any recognised admin groups | ||||||
|  | 	var admin bool | ||||||
|  | 	for _, g := range claims.Groups { | ||||||
|  | 		if strings.EqualFold(g, "admin") || strings.EqualFold(g, "admins") { | ||||||
|  | 			admin = true | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// we still need to set *a* password even if it's not a password the user will end up using, so set something random | ||||||
|  | 	// in this case, we'll just set two uuids on top of each other, which should be long + random enough to baffle any attempts to crack. | ||||||
|  | 	// | ||||||
|  | 	// if the user ever wants to log in using gts password rather than oidc flow, they'll have to request a password reset, which is fine | ||||||
|  | 	password := uuid.NewString() + uuid.NewString() | ||||||
|  | 
 | ||||||
|  | 	// create the user! this will also create an account and store it in the database so we don't need to do that here | ||||||
|  | 	user, err = m.db.NewSignup(username, "", m.config.AccountsConfig.RequireApproval, claims.Email, password, ip, "", appID, claims.EmailVerified, admin) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("error creating user: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return user, nil | ||||||
|  | 
 | ||||||
|  | } | ||||||
|  | @ -39,7 +39,24 @@ type login struct { | ||||||
| // The idea is to present a sign in page to the user, where they can enter their username and password. | // The idea is to present a sign in page to the user, where they can enter their username and password. | ||||||
| // The form will then POST to the sign in page, which will be handled by SignInPOSTHandler | // The form will then POST to the sign in page, which will be handled by SignInPOSTHandler | ||||||
| func (m *Module) SignInGETHandler(c *gin.Context) { | func (m *Module) SignInGETHandler(c *gin.Context) { | ||||||
| 	m.log.WithField("func", "SignInGETHandler").Trace("serving sign in html") | 	l := m.log.WithField("func", "SignInGETHandler") | ||||||
|  | 	l.Trace("entering sign in handler") | ||||||
|  | 	if m.idp != nil { | ||||||
|  | 		s := sessions.Default(c) | ||||||
|  | 
 | ||||||
|  | 		stateI := s.Get(sessionState) | ||||||
|  | 		state, ok := stateI.(string) | ||||||
|  | 		if !ok { | ||||||
|  | 			m.clearSession(s) | ||||||
|  | 			c.JSON(http.StatusForbidden, gin.H{"error": "state not found in session"}) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  | 
 | ||||||
|  | 		redirect := m.idp.AuthCodeURL(state) | ||||||
|  | 		l.Debugf("redirecting to external idp at %s", redirect) | ||||||
|  | 		c.Redirect(http.StatusSeeOther, redirect) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
| 	c.HTML(http.StatusOK, "sign-in.tmpl", gin.H{}) | 	c.HTML(http.StatusOK, "sign-in.tmpl", gin.H{}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -52,6 +69,7 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) { | ||||||
| 	form := &login{} | 	form := &login{} | ||||||
| 	if err := c.ShouldBind(form); err != nil { | 	if err := c.ShouldBind(form); err != nil { | ||||||
| 		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | 		c.JSON(http.StatusBadRequest, gin.H{"error": err.Error()}) | ||||||
|  | 		m.clearSession(s) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 	l.Tracef("parsed form: %+v", form) | 	l.Tracef("parsed form: %+v", form) | ||||||
|  | @ -59,12 +77,14 @@ func (m *Module) SignInPOSTHandler(c *gin.Context) { | ||||||
| 	userid, err := m.ValidatePassword(form.Email, form.Password) | 	userid, err := m.ValidatePassword(form.Email, form.Password) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		c.String(http.StatusForbidden, err.Error()) | 		c.String(http.StatusForbidden, err.Error()) | ||||||
|  | 		m.clearSession(s) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	s.Set(sessionUserID, userid) | 	s.Set(sessionUserID, userid) | ||||||
| 	if err := s.Save(); err != nil { | 	if err := s.Save(); err != nil { | ||||||
| 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | 		c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) | ||||||
|  | 		m.clearSession(s) | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
							
								
								
									
										17
									
								
								internal/api/client/auth/util.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								internal/api/client/auth/util.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | ||||||
|  | package auth | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"github.com/gin-contrib/sessions" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (m *Module) clearSession(s sessions.Session) { | ||||||
|  | 	s.Clear() | ||||||
|  | 
 | ||||||
|  | 	// newOptions := router.SessionOptions(m.config) | ||||||
|  | 	// newOptions.MaxAge = -1 // instruct browser to delete cookie immediately | ||||||
|  | 	// s.Options(newOptions) | ||||||
|  | 
 | ||||||
|  | 	if err := s.Save(); err != nil { | ||||||
|  | 		panic(err) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | @ -64,7 +64,7 @@ var Create cliactions.GTSAction = func(ctx context.Context, c *config.Config, lo | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	_, err = dbConn.NewSignup(username, "", false, email, password, nil, "", "") | 	_, err = dbConn.NewSignup(username, "", false, email, password, nil, "", "", false, false) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return err | 		return err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -42,6 +42,7 @@ import ( | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | 	"github.com/superseriousbusiness/gotosocial/internal/gtsmodel" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/media" | 	"github.com/superseriousbusiness/gotosocial/internal/media" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | 	"github.com/superseriousbusiness/gotosocial/internal/oauth" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/oidc" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/processing" | 	"github.com/superseriousbusiness/gotosocial/internal/processing" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/router" | 	"github.com/superseriousbusiness/gotosocial/internal/router" | ||||||
| 	timelineprocessing "github.com/superseriousbusiness/gotosocial/internal/timeline" | 	timelineprocessing "github.com/superseriousbusiness/gotosocial/internal/timeline" | ||||||
|  | @ -121,8 +122,13 @@ var Start cliactions.GTSAction = func(ctx context.Context, c *config.Config, log | ||||||
| 		return fmt.Errorf("error starting processor: %s", err) | 		return fmt.Errorf("error starting processor: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	idp, err := oidc.NewIDP(c, log) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("error creating oidc idp: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// build client api modules | 	// build client api modules | ||||||
| 	authModule := auth.New(c, dbService, oauthServer, log) | 	authModule := auth.New(c, dbService, oauthServer, idp, log) | ||||||
| 	accountModule := account.New(c, processor, log) | 	accountModule := account.New(c, processor, log) | ||||||
| 	instanceModule := instance.New(c, processor, log) | 	instanceModule := instance.New(c, processor, log) | ||||||
| 	appsModule := app.New(c, processor, log) | 	appsModule := app.New(c, processor, log) | ||||||
|  |  | ||||||
|  | @ -37,6 +37,7 @@ import ( | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/cliactions" | 	"github.com/superseriousbusiness/gotosocial/internal/cliactions" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/config" | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/gotosocial" | 	"github.com/superseriousbusiness/gotosocial/internal/gotosocial" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/oidc" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/web" | 	"github.com/superseriousbusiness/gotosocial/internal/web" | ||||||
| 	"github.com/superseriousbusiness/gotosocial/testrig" | 	"github.com/superseriousbusiness/gotosocial/testrig" | ||||||
| ) | ) | ||||||
|  | @ -66,8 +67,13 @@ var Start cliactions.GTSAction = func(ctx context.Context, _ *config.Config, log | ||||||
| 		return fmt.Errorf("error starting processor: %s", err) | 		return fmt.Errorf("error starting processor: %s", err) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
|  | 	idp, err := oidc.NewIDP(c, log) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return fmt.Errorf("error creating oidc idp: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	// build client api modules | 	// build client api modules | ||||||
| 	authModule := auth.New(c, dbService, oauthServer, log) | 	authModule := auth.New(c, dbService, oauthServer, idp, log) | ||||||
| 	accountModule := account.New(c, processor, log) | 	accountModule := account.New(c, processor, log) | ||||||
| 	instanceModule := instance.New(c, processor, log) | 	instanceModule := instance.New(c, processor, log) | ||||||
| 	appsModule := app.New(c, processor, log) | 	appsModule := app.New(c, processor, log) | ||||||
|  |  | ||||||
|  | @ -57,6 +57,7 @@ type Config struct { | ||||||
| 	StorageConfig     *StorageConfig     `yaml:"storage"` | 	StorageConfig     *StorageConfig     `yaml:"storage"` | ||||||
| 	StatusesConfig    *StatusesConfig    `yaml:"statuses"` | 	StatusesConfig    *StatusesConfig    `yaml:"statuses"` | ||||||
| 	LetsEncryptConfig *LetsEncryptConfig `yaml:"letsEncrypt"` | 	LetsEncryptConfig *LetsEncryptConfig `yaml:"letsEncrypt"` | ||||||
|  | 	OIDCConfig        *OIDCConfig        `yaml:"oidc"` | ||||||
| 
 | 
 | ||||||
| 	/* | 	/* | ||||||
| 		Not parsed from .yaml configuration file. | 		Not parsed from .yaml configuration file. | ||||||
|  | @ -87,6 +88,7 @@ func Empty() *Config { | ||||||
| 		StorageConfig:     &StorageConfig{}, | 		StorageConfig:     &StorageConfig{}, | ||||||
| 		StatusesConfig:    &StatusesConfig{}, | 		StatusesConfig:    &StatusesConfig{}, | ||||||
| 		LetsEncryptConfig: &LetsEncryptConfig{}, | 		LetsEncryptConfig: &LetsEncryptConfig{}, | ||||||
|  | 		OIDCConfig:        &OIDCConfig{}, | ||||||
| 		AccountCLIFlags:   make(map[string]string), | 		AccountCLIFlags:   make(map[string]string), | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | @ -268,7 +270,34 @@ func (c *Config) ParseCLIFlags(f KeyedFlags, version string) error { | ||||||
| 		c.LetsEncryptConfig.EmailAddress = f.String(fn.LetsEncryptEmailAddress) | 		c.LetsEncryptConfig.EmailAddress = f.String(fn.LetsEncryptEmailAddress) | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	c.SoftwareVersion = GetDefaults().SoftwareVersion | 	// OIDC flags | ||||||
|  | 	if f.IsSet(fn.OIDCEnabled) { | ||||||
|  | 		c.OIDCConfig.Enabled = f.Bool(fn.OIDCEnabled) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if c.OIDCConfig.IDPName == "" || f.IsSet(fn.OIDCIdpName) { | ||||||
|  | 		c.OIDCConfig.IDPName = f.String(fn.OIDCIdpName) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if f.IsSet(fn.OIDCSkipVerification) { | ||||||
|  | 		c.OIDCConfig.SkipVerification = f.Bool(fn.OIDCSkipVerification) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if c.OIDCConfig.Issuer == "" || f.IsSet(fn.OIDCIssuer) { | ||||||
|  | 		c.OIDCConfig.Issuer = f.String(fn.OIDCIssuer) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if c.OIDCConfig.ClientID == "" || f.IsSet(fn.OIDCClientID) { | ||||||
|  | 		c.OIDCConfig.ClientID = f.String(fn.OIDCClientID) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if c.OIDCConfig.ClientSecret == "" || f.IsSet(fn.OIDCClientSecret) { | ||||||
|  | 		c.OIDCConfig.ClientSecret = f.String(fn.OIDCClientSecret) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if len(c.OIDCConfig.Scopes) == 0 || f.IsSet(fn.OIDCScopes) { | ||||||
|  | 		c.OIDCConfig.Scopes = f.StringSlice(fn.OIDCScopes) | ||||||
|  | 	} | ||||||
| 
 | 
 | ||||||
| 	// command-specific flags | 	// command-specific flags | ||||||
| 
 | 
 | ||||||
|  | @ -278,7 +307,6 @@ func (c *Config) ParseCLIFlags(f KeyedFlags, version string) error { | ||||||
| 	c.AccountCLIFlags[PasswordFlag] = f.String(PasswordFlag) | 	c.AccountCLIFlags[PasswordFlag] = f.String(PasswordFlag) | ||||||
| 
 | 
 | ||||||
| 	c.SoftwareVersion = version | 	c.SoftwareVersion = version | ||||||
| 
 |  | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -287,6 +315,7 @@ func (c *Config) ParseCLIFlags(f KeyedFlags, version string) error { | ||||||
| type KeyedFlags interface { | type KeyedFlags interface { | ||||||
| 	Bool(k string) bool | 	Bool(k string) bool | ||||||
| 	String(k string) string | 	String(k string) string | ||||||
|  | 	StringSlice(k string) []string | ||||||
| 	Int(k string) int | 	Int(k string) int | ||||||
| 	IsSet(k string) bool | 	IsSet(k string) bool | ||||||
| } | } | ||||||
|  | @ -337,6 +366,14 @@ type Flags struct { | ||||||
| 	LetsEncryptEnabled      string | 	LetsEncryptEnabled      string | ||||||
| 	LetsEncryptCertDir      string | 	LetsEncryptCertDir      string | ||||||
| 	LetsEncryptEmailAddress string | 	LetsEncryptEmailAddress string | ||||||
|  | 
 | ||||||
|  | 	OIDCEnabled          string | ||||||
|  | 	OIDCIdpName          string | ||||||
|  | 	OIDCSkipVerification string | ||||||
|  | 	OIDCIssuer           string | ||||||
|  | 	OIDCClientID         string | ||||||
|  | 	OIDCClientSecret     string | ||||||
|  | 	OIDCScopes           string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // Defaults contains all the default values for a gotosocial config | // Defaults contains all the default values for a gotosocial config | ||||||
|  | @ -385,6 +422,14 @@ type Defaults struct { | ||||||
| 	LetsEncryptEnabled      bool | 	LetsEncryptEnabled      bool | ||||||
| 	LetsEncryptCertDir      string | 	LetsEncryptCertDir      string | ||||||
| 	LetsEncryptEmailAddress string | 	LetsEncryptEmailAddress string | ||||||
|  | 
 | ||||||
|  | 	OIDCEnabled          bool | ||||||
|  | 	OIDCIdpName          string | ||||||
|  | 	OIDCSkipVerification bool | ||||||
|  | 	OIDCIssuer           string | ||||||
|  | 	OIDCClientID         string | ||||||
|  | 	OIDCClientSecret     string | ||||||
|  | 	OIDCScopes           []string | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // GetFlagNames returns a struct containing the names of the various flags used for | // GetFlagNames returns a struct containing the names of the various flags used for | ||||||
|  | @ -434,6 +479,14 @@ func GetFlagNames() Flags { | ||||||
| 		LetsEncryptEnabled:      "letsencrypt-enabled", | 		LetsEncryptEnabled:      "letsencrypt-enabled", | ||||||
| 		LetsEncryptCertDir:      "letsencrypt-cert-dir", | 		LetsEncryptCertDir:      "letsencrypt-cert-dir", | ||||||
| 		LetsEncryptEmailAddress: "letsencrypt-email", | 		LetsEncryptEmailAddress: "letsencrypt-email", | ||||||
|  | 
 | ||||||
|  | 		OIDCEnabled:          "oidc-enabled", | ||||||
|  | 		OIDCIdpName:          "oidc-idp-name", | ||||||
|  | 		OIDCSkipVerification: "oidc-skip-verification", | ||||||
|  | 		OIDCIssuer:           "oidc-issuer", | ||||||
|  | 		OIDCClientID:         "oidc-client-id", | ||||||
|  | 		OIDCClientSecret:     "oidc-client-secret", | ||||||
|  | 		OIDCScopes:           "oidc-scopes", | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -484,5 +537,13 @@ func GetEnvNames() Flags { | ||||||
| 		LetsEncryptEnabled:      "GTS_LETSENCRYPT_ENABLED", | 		LetsEncryptEnabled:      "GTS_LETSENCRYPT_ENABLED", | ||||||
| 		LetsEncryptCertDir:      "GTS_LETSENCRYPT_CERT_DIR", | 		LetsEncryptCertDir:      "GTS_LETSENCRYPT_CERT_DIR", | ||||||
| 		LetsEncryptEmailAddress: "GTS_LETSENCRYPT_EMAIL", | 		LetsEncryptEmailAddress: "GTS_LETSENCRYPT_EMAIL", | ||||||
|  | 
 | ||||||
|  | 		OIDCEnabled:          "GTS_OIDC_ENABLED", | ||||||
|  | 		OIDCIdpName:          "GTS_OIDC_IDP_NAME", | ||||||
|  | 		OIDCSkipVerification: "GTS_OIDC_SKIP_VERIFICATION", | ||||||
|  | 		OIDCIssuer:           "GTS_OIDC_ISSUER", | ||||||
|  | 		OIDCClientID:         "GTS_OIDC_CLIENT_ID", | ||||||
|  | 		OIDCClientSecret:     "GTS_OIDC_CLIENT_SECRET", | ||||||
|  | 		OIDCScopes:           "GTS_OIDC_SCOPES", | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -1,5 +1,7 @@ | ||||||
| package config | package config | ||||||
| 
 | 
 | ||||||
|  | import "github.com/coreos/go-oidc/v3/oidc" | ||||||
|  | 
 | ||||||
| // TestDefault returns a default config for testing | // TestDefault returns a default config for testing | ||||||
| func TestDefault() *Config { | func TestDefault() *Config { | ||||||
| 	defaults := GetTestDefaults() | 	defaults := GetTestDefaults() | ||||||
|  | @ -52,6 +54,15 @@ func TestDefault() *Config { | ||||||
| 			CertDir:      defaults.LetsEncryptCertDir, | 			CertDir:      defaults.LetsEncryptCertDir, | ||||||
| 			EmailAddress: defaults.LetsEncryptEmailAddress, | 			EmailAddress: defaults.LetsEncryptEmailAddress, | ||||||
| 		}, | 		}, | ||||||
|  | 		OIDCConfig: &OIDCConfig{ | ||||||
|  | 			Enabled:          defaults.OIDCEnabled, | ||||||
|  | 			IDPName:          defaults.OIDCIdpName, | ||||||
|  | 			SkipVerification: defaults.OIDCSkipVerification, | ||||||
|  | 			Issuer:           defaults.OIDCIssuer, | ||||||
|  | 			ClientID:         defaults.OIDCClientID, | ||||||
|  | 			ClientSecret:     defaults.OIDCClientSecret, | ||||||
|  | 			Scopes:           defaults.OIDCScopes, | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -107,6 +118,15 @@ func Default() *Config { | ||||||
| 			CertDir:      defaults.LetsEncryptCertDir, | 			CertDir:      defaults.LetsEncryptCertDir, | ||||||
| 			EmailAddress: defaults.LetsEncryptEmailAddress, | 			EmailAddress: defaults.LetsEncryptEmailAddress, | ||||||
| 		}, | 		}, | ||||||
|  | 		OIDCConfig: &OIDCConfig{ | ||||||
|  | 			Enabled:          defaults.OIDCEnabled, | ||||||
|  | 			IDPName:          defaults.OIDCIdpName, | ||||||
|  | 			SkipVerification: defaults.OIDCSkipVerification, | ||||||
|  | 			Issuer:           defaults.OIDCIssuer, | ||||||
|  | 			ClientID:         defaults.OIDCClientID, | ||||||
|  | 			ClientSecret:     defaults.OIDCClientSecret, | ||||||
|  | 			Scopes:           defaults.OIDCScopes, | ||||||
|  | 		}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -157,6 +177,14 @@ func GetDefaults() Defaults { | ||||||
| 		LetsEncryptEnabled:      true, | 		LetsEncryptEnabled:      true, | ||||||
| 		LetsEncryptCertDir:      "/gotosocial/storage/certs", | 		LetsEncryptCertDir:      "/gotosocial/storage/certs", | ||||||
| 		LetsEncryptEmailAddress: "", | 		LetsEncryptEmailAddress: "", | ||||||
|  | 
 | ||||||
|  | 		OIDCEnabled:          false, | ||||||
|  | 		OIDCIdpName:          "", | ||||||
|  | 		OIDCSkipVerification: false, | ||||||
|  | 		OIDCIssuer:           "", | ||||||
|  | 		OIDCClientID:         "", | ||||||
|  | 		OIDCClientSecret:     "", | ||||||
|  | 		OIDCScopes:           []string{oidc.ScopeOpenID, "profile", "email", "groups"}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -204,5 +232,13 @@ func GetTestDefaults() Defaults { | ||||||
| 		LetsEncryptEnabled:      false, | 		LetsEncryptEnabled:      false, | ||||||
| 		LetsEncryptCertDir:      "", | 		LetsEncryptCertDir:      "", | ||||||
| 		LetsEncryptEmailAddress: "", | 		LetsEncryptEmailAddress: "", | ||||||
|  | 
 | ||||||
|  | 		OIDCEnabled:          false, | ||||||
|  | 		OIDCIdpName:          "", | ||||||
|  | 		OIDCSkipVerification: false, | ||||||
|  | 		OIDCIssuer:           "", | ||||||
|  | 		OIDCClientID:         "", | ||||||
|  | 		OIDCClientSecret:     "", | ||||||
|  | 		OIDCScopes:           []string{oidc.ScopeOpenID, "profile", "email", "groups"}, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  |  | ||||||
							
								
								
									
										30
									
								
								internal/config/oidc.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								internal/config/oidc.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,30 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package config | ||||||
|  | 
 | ||||||
|  | // OIDCConfig contains configuration values for openID connect (oauth) authorization by an external service such as Dex. | ||||||
|  | type OIDCConfig struct { | ||||||
|  | 	Enabled          bool     `yaml:"enabled"` | ||||||
|  | 	IDPName          string   `yaml:"idpName"` | ||||||
|  | 	SkipVerification bool     `yaml:"skipVerification"` | ||||||
|  | 	Issuer           string   `yaml:"issuer"` | ||||||
|  | 	ClientID         string   `yaml:"clientID"` | ||||||
|  | 	ClientSecret     string   `yaml:"clientSecret"` | ||||||
|  | 	Scopes           []string `yaml:"scopes"` | ||||||
|  | } | ||||||
|  | @ -177,9 +177,9 @@ type DB interface { | ||||||
| 	// C) something went wrong in the db | 	// C) something went wrong in the db | ||||||
| 	IsEmailAvailable(email string) error | 	IsEmailAvailable(email string) error | ||||||
| 
 | 
 | ||||||
| 	// NewSignup creates a new user in the database with the given parameters, with an *unconfirmed* email address. | 	// NewSignup creates a new user in the database with the given parameters. | ||||||
| 	// By the time this function is called, it should be assumed that all the parameters have passed validation! | 	// By the time this function is called, it should be assumed that all the parameters have passed validation! | ||||||
| 	NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*gtsmodel.User, error) | 	NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, admin bool) (*gtsmodel.User, error) | ||||||
| 
 | 
 | ||||||
| 	// SetHeaderOrAvatarForAccountID sets the header or avatar for the given accountID to the given media attachment. | 	// SetHeaderOrAvatarForAccountID sets the header or avatar for the given accountID to the given media attachment. | ||||||
| 	SetHeaderOrAvatarForAccountID(mediaAttachment *gtsmodel.MediaAttachment, accountID string) error | 	SetHeaderOrAvatarForAccountID(mediaAttachment *gtsmodel.MediaAttachment, accountID string) error | ||||||
|  |  | ||||||
|  | @ -548,38 +548,49 @@ func (ps *postgresService) IsEmailAvailable(email string) error { | ||||||
| 	return nil | 	return nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (ps *postgresService) NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string) (*gtsmodel.User, error) { | func (ps *postgresService) NewSignup(username string, reason string, requireApproval bool, email string, password string, signUpIP net.IP, locale string, appID string, emailVerified bool, admin bool) (*gtsmodel.User, error) { | ||||||
| 	key, err := rsa.GenerateKey(rand.Reader, 2048) | 	key, err := rsa.GenerateKey(rand.Reader, 2048) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		ps.log.Errorf("error creating new rsa key: %s", err) | 		ps.log.Errorf("error creating new rsa key: %s", err) | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	newAccountURIs := util.GenerateURIsForAccount(username, ps.config.Protocol, ps.config.Host) | 	// if something went wrong while creating a user, we might already have an account, so check here first... | ||||||
| 	newAccountID, err := id.NewRandomULID() | 	a := >smodel.Account{} | ||||||
|  | 	err = ps.conn.Model(a).Where("username = ?", username).Where("? IS NULL", pg.Ident("domain")).Select() | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, err | 		// there's been an actual error | ||||||
| 	} | 		if err != pg.ErrNoRows { | ||||||
|  | 			return nil, fmt.Errorf("db error checking existence of account: %s", err) | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
| 	a := >smodel.Account{ | 		// we just don't have an account yet create one | ||||||
| 		ID:                    newAccountID, | 		newAccountURIs := util.GenerateURIsForAccount(username, ps.config.Protocol, ps.config.Host) | ||||||
| 		Username:              username, | 		newAccountID, err := id.NewRandomULID() | ||||||
| 		DisplayName:           username, | 		if err != nil { | ||||||
| 		Reason:                reason, | 			return nil, err | ||||||
| 		URL:                   newAccountURIs.UserURL, | 		} | ||||||
| 		PrivateKey:            key, | 
 | ||||||
| 		PublicKey:             &key.PublicKey, | 		a = >smodel.Account{ | ||||||
| 		PublicKeyURI:          newAccountURIs.PublicKeyURI, | 			ID:                    newAccountID, | ||||||
| 		ActorType:             gtsmodel.ActivityStreamsPerson, | 			Username:              username, | ||||||
| 		URI:                   newAccountURIs.UserURI, | 			DisplayName:           username, | ||||||
| 		InboxURI:              newAccountURIs.InboxURI, | 			Reason:                reason, | ||||||
| 		OutboxURI:             newAccountURIs.OutboxURI, | 			URL:                   newAccountURIs.UserURL, | ||||||
| 		FollowersURI:          newAccountURIs.FollowersURI, | 			PrivateKey:            key, | ||||||
| 		FollowingURI:          newAccountURIs.FollowingURI, | 			PublicKey:             &key.PublicKey, | ||||||
| 		FeaturedCollectionURI: newAccountURIs.CollectionURI, | 			PublicKeyURI:          newAccountURIs.PublicKeyURI, | ||||||
| 	} | 			ActorType:             gtsmodel.ActivityStreamsPerson, | ||||||
| 	if _, err = ps.conn.Model(a).Insert(); err != nil { | 			URI:                   newAccountURIs.UserURI, | ||||||
| 		return nil, err | 			InboxURI:              newAccountURIs.InboxURI, | ||||||
|  | 			OutboxURI:             newAccountURIs.OutboxURI, | ||||||
|  | 			FollowersURI:          newAccountURIs.FollowersURI, | ||||||
|  | 			FollowingURI:          newAccountURIs.FollowingURI, | ||||||
|  | 			FeaturedCollectionURI: newAccountURIs.CollectionURI, | ||||||
|  | 		} | ||||||
|  | 		if _, err = ps.conn.Model(a).Insert(); err != nil { | ||||||
|  | 			return nil, err | ||||||
|  | 		} | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) | 	pw, err := bcrypt.GenerateFromPassword([]byte(password), bcrypt.DefaultCost) | ||||||
|  | @ -594,14 +605,25 @@ func (ps *postgresService) NewSignup(username string, reason string, requireAppr | ||||||
| 
 | 
 | ||||||
| 	u := >smodel.User{ | 	u := >smodel.User{ | ||||||
| 		ID:                     newUserID, | 		ID:                     newUserID, | ||||||
| 		AccountID:              newAccountID, | 		AccountID:              a.ID, | ||||||
| 		EncryptedPassword:      string(pw), | 		EncryptedPassword:      string(pw), | ||||||
| 		SignUpIP:               signUpIP, | 		SignUpIP:               signUpIP.To4(), | ||||||
| 		Locale:                 locale, | 		Locale:                 locale, | ||||||
| 		UnconfirmedEmail:       email, | 		UnconfirmedEmail:       email, | ||||||
| 		CreatedByApplicationID: appID, | 		CreatedByApplicationID: appID, | ||||||
| 		Approved:               !requireApproval, // if we don't require moderator approval, just pre-approve the user | 		Approved:               !requireApproval, // if we don't require moderator approval, just pre-approve the user | ||||||
| 	} | 	} | ||||||
|  | 
 | ||||||
|  | 	if emailVerified { | ||||||
|  | 		u.ConfirmedAt = time.Now() | ||||||
|  | 		u.Email = email | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	if admin { | ||||||
|  | 		u.Admin = true | ||||||
|  | 		u.Moderator = true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
| 	if _, err = ps.conn.Model(u).Insert(); err != nil { | 	if _, err = ps.conn.Model(u).Insert(); err != nil { | ||||||
| 		return nil, err | 		return nil, err | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
							
								
								
									
										27
									
								
								internal/oidc/claims.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								internal/oidc/claims.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package oidc | ||||||
|  | 
 | ||||||
|  | // Claims represents claims as found in an id_token returned from an OIDC flow. | ||||||
|  | type Claims struct { | ||||||
|  | 	Email         string   `json:"email"` | ||||||
|  | 	EmailVerified bool     `json:"email_verified"` | ||||||
|  | 	Groups        []string `json:"groups"` | ||||||
|  | 	Name          string   `json:"name"` | ||||||
|  | } | ||||||
							
								
								
									
										65
									
								
								internal/oidc/handlecallback.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										65
									
								
								internal/oidc/handlecallback.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,65 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package oidc | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"errors" | ||||||
|  | 	"fmt" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func (i *idp) HandleCallback(ctx context.Context, code string) (*Claims, error) { | ||||||
|  | 	l := i.log.WithField("func", "HandleCallback") | ||||||
|  | 	if code == "" { | ||||||
|  | 		return nil, errors.New("code was empty string") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	l.Debug("exchanging code for oauth2token") | ||||||
|  | 	oauth2Token, err := i.oauth2Config.Exchange(ctx, code) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("error exchanging code for oauth2token: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	l.Debug("extracting id_token") | ||||||
|  | 	rawIDToken, ok := oauth2Token.Extra("id_token").(string) | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil, errors.New("no id_token in oauth2token") | ||||||
|  | 	} | ||||||
|  | 	l.Debugf("raw id token: %s", rawIDToken) | ||||||
|  | 
 | ||||||
|  | 	// Parse and verify ID Token payload. | ||||||
|  | 	l.Debug("verifying id_token") | ||||||
|  | 	idTokenVerifier := i.provider.Verifier(i.oidcConf) | ||||||
|  | 	idToken, err := idTokenVerifier.Verify(ctx, rawIDToken) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, fmt.Errorf("could not verify id token: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	l.Debug("extracting claims from id_token") | ||||||
|  | 	claims := &Claims{} | ||||||
|  | 	if err := idToken.Claims(claims); err != nil { | ||||||
|  | 		return nil, fmt.Errorf("could not parse claims from idToken: %s", err) | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return claims, nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (i *idp) AuthCodeURL(state string) string { | ||||||
|  | 	return i.oauth2Config.AuthCodeURL(state) | ||||||
|  | } | ||||||
							
								
								
									
										121
									
								
								internal/oidc/idp.go
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										121
									
								
								internal/oidc/idp.go
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,121 @@ | ||||||
|  | /* | ||||||
|  |    GoToSocial | ||||||
|  |    Copyright (C) 2021 GoToSocial Authors admin@gotosocial.org | ||||||
|  | 
 | ||||||
|  |    This program is free software: you can redistribute it and/or modify | ||||||
|  |    it under the terms of the GNU Affero General Public License as published by | ||||||
|  |    the Free Software Foundation, either version 3 of the License, or | ||||||
|  |    (at your option) any later version. | ||||||
|  | 
 | ||||||
|  |    This program is distributed in the hope that it will be useful, | ||||||
|  |    but WITHOUT ANY WARRANTY; without even the implied warranty of | ||||||
|  |    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the | ||||||
|  |    GNU Affero General Public License for more details. | ||||||
|  | 
 | ||||||
|  |    You should have received a copy of the GNU Affero General Public License | ||||||
|  |    along with this program.  If not, see <http://www.gnu.org/licenses/>. | ||||||
|  | */ | ||||||
|  | 
 | ||||||
|  | package oidc | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"context" | ||||||
|  | 	"fmt" | ||||||
|  | 
 | ||||||
|  | 	"github.com/coreos/go-oidc/v3/oidc" | ||||||
|  | 	"github.com/sirupsen/logrus" | ||||||
|  | 	"github.com/superseriousbusiness/gotosocial/internal/config" | ||||||
|  | 	"golang.org/x/oauth2" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	// CallbackPath is the API path for receiving callback tokens from external OIDC providers | ||||||
|  | 	CallbackPath = "/auth/callback" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // IDP contains logic for parsing an OIDC access code into a set of claims by calling an external OIDC provider. | ||||||
|  | type IDP interface { | ||||||
|  | 	// HandleCallback accepts a context (pass the context from the http.Request), and an oauth2 code as returned from a successful | ||||||
|  | 	// login through an OIDC provider. It uses the code to request a token from the OIDC provider, which should contain an id_token | ||||||
|  | 	// with a set of claims. | ||||||
|  | 	// | ||||||
|  | 	// Note that this function *does not* verify state. That should be handled by the caller *before* this function is called. | ||||||
|  | 	HandleCallback(ctx context.Context, code string) (*Claims, error) | ||||||
|  | 	// AuthCodeURL returns the proper redirect URL for this IDP, for redirecting requesters to the correct OIDC endpoint. | ||||||
|  | 	AuthCodeURL(state string) string | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type idp struct { | ||||||
|  | 	oauth2Config oauth2.Config | ||||||
|  | 	provider     *oidc.Provider | ||||||
|  | 	oidcConf     *oidc.Config | ||||||
|  | 	log          *logrus.Logger | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // NewIDP returns a new IDP configured with the given config and logger. | ||||||
|  | // If the passed config contains a nil value for the OIDCConfig, or OIDCConfig.Enabled | ||||||
|  | // is set to false, then nil, nil will be returned. If OIDCConfig.Enabled is true, | ||||||
|  | // then the other OIDC config fields must also be set. | ||||||
|  | func NewIDP(config *config.Config, log *logrus.Logger) (IDP, error) { | ||||||
|  | 
 | ||||||
|  | 	// oidc isn't enabled so we don't need to do anything | ||||||
|  | 	if config.OIDCConfig == nil || !config.OIDCConfig.Enabled { | ||||||
|  | 		return nil, nil | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// validate config fields | ||||||
|  | 	if config.OIDCConfig.IDPName == "" { | ||||||
|  | 		return nil, fmt.Errorf("not set: IDPName") | ||||||
|  | 	} | ||||||
|  | 	if config.OIDCConfig.Issuer == "" { | ||||||
|  | 		return nil, fmt.Errorf("not set: Issuer") | ||||||
|  | 	} | ||||||
|  | 	if config.OIDCConfig.ClientID == "" { | ||||||
|  | 		return nil, fmt.Errorf("not set: ClientID") | ||||||
|  | 	} | ||||||
|  | 	if config.OIDCConfig.ClientSecret == "" { | ||||||
|  | 		return nil, fmt.Errorf("not set: ClientSecret") | ||||||
|  | 	} | ||||||
|  | 	if len(config.OIDCConfig.Scopes) == 0 { | ||||||
|  | 		return nil, fmt.Errorf("not set: Scopes") | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	provider, err := oidc.NewProvider(context.Background(), config.OIDCConfig.Issuer) | ||||||
|  | 	if err != nil { | ||||||
|  | 		return nil, err | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	oauth2Config := oauth2.Config{ | ||||||
|  | 		// client_id and client_secret of the client. | ||||||
|  | 		ClientID:     config.OIDCConfig.ClientID, | ||||||
|  | 		ClientSecret: config.OIDCConfig.ClientSecret, | ||||||
|  | 
 | ||||||
|  | 		// The redirectURL. | ||||||
|  | 		RedirectURL: fmt.Sprintf("%s://%s%s", config.Protocol, config.Host, CallbackPath), | ||||||
|  | 
 | ||||||
|  | 		// Discovery returns the OAuth2 endpoints. | ||||||
|  | 		Endpoint: provider.Endpoint(), | ||||||
|  | 
 | ||||||
|  | 		// "openid" is a required scope for OpenID Connect flows. | ||||||
|  | 		// | ||||||
|  | 		// Other scopes, such as "groups" can be requested. | ||||||
|  | 		Scopes: config.OIDCConfig.Scopes, | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	// create a config for verifier creation | ||||||
|  | 	oidcConf := &oidc.Config{ | ||||||
|  | 		ClientID: config.OIDCConfig.ClientID, | ||||||
|  | 	} | ||||||
|  | 	if config.OIDCConfig.SkipVerification { | ||||||
|  | 		oidcConf.SkipClientIDCheck = true | ||||||
|  | 		oidcConf.SkipExpiryCheck = true | ||||||
|  | 		oidcConf.SkipIssuerCheck = true | ||||||
|  | 	} | ||||||
|  | 
 | ||||||
|  | 	return &idp{ | ||||||
|  | 		oauth2Config: oauth2Config, | ||||||
|  | 		oidcConf:     oidcConf, | ||||||
|  | 		provider:     provider, | ||||||
|  | 		log:          log, | ||||||
|  | 	}, nil | ||||||
|  | } | ||||||
|  | @ -45,7 +45,7 @@ func (p *processor) Create(applicationToken oauth2.TokenInfo, application *gtsmo | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	l.Trace("creating new username and account") | 	l.Trace("creating new username and account") | ||||||
| 	user, err := p.db.NewSignup(form.Username, util.RemoveHTML(reason), p.config.AccountsConfig.RequireApproval, form.Email, form.Password, form.IP, form.Locale, application.ID) | 	user, err := p.db.NewSignup(form.Username, util.RemoveHTML(reason), p.config.AccountsConfig.RequireApproval, form.Email, form.Password, form.IP, form.Locale, application.ID, false, false) | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return nil, fmt.Errorf("error creating new signup in the database: %s", err) | 		return nil, fmt.Errorf("error creating new signup in the database: %s", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -33,6 +33,18 @@ import ( | ||||||
| 	"github.com/superseriousbusiness/gotosocial/internal/id" | 	"github.com/superseriousbusiness/gotosocial/internal/id" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // SessionOptions returns the standard set of options to use for each session. | ||||||
|  | func SessionOptions(cfg *config.Config) sessions.Options { | ||||||
|  | 	return sessions.Options{ | ||||||
|  | 		Path:     "/", | ||||||
|  | 		Domain:   cfg.Host, | ||||||
|  | 		MaxAge:   120,                      // 2 minutes | ||||||
|  | 		Secure:   true,                     // only use cookie over https | ||||||
|  | 		HttpOnly: true,                     // exclude javascript from inspecting cookie | ||||||
|  | 		SameSite: http.SameSiteDefaultMode, // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1 | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func useSession(cfg *config.Config, dbService db.DB, engine *gin.Engine) error { | func useSession(cfg *config.Config, dbService db.DB, engine *gin.Engine) error { | ||||||
| 	// check if we have a saved router session already | 	// check if we have a saved router session already | ||||||
| 	routerSessions := []*gtsmodel.RouterSession{} | 	routerSessions := []*gtsmodel.RouterSession{} | ||||||
|  | @ -64,14 +76,7 @@ func useSession(cfg *config.Config, dbService db.DB, engine *gin.Engine) error { | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	store := memstore.NewStore(rs.Auth, rs.Crypt) | 	store := memstore.NewStore(rs.Auth, rs.Crypt) | ||||||
| 	store.Options(sessions.Options{ | 	store.Options(SessionOptions(cfg)) | ||||||
| 		Path:     "/", |  | ||||||
| 		Domain:   cfg.Host, |  | ||||||
| 		MaxAge:   120,                     // 2 minutes |  | ||||||
| 		Secure:   true,                    // only use cookie over https |  | ||||||
| 		HttpOnly: true,                    // exclude javascript from inspecting cookie |  | ||||||
| 		SameSite: http.SameSiteStrictMode, // https://datatracker.ietf.org/doc/html/draft-ietf-httpbis-cookie-same-site-00#section-4.1.1 |  | ||||||
| 	}) |  | ||||||
| 	sessionName := fmt.Sprintf("gotosocial-%s", cfg.Host) | 	sessionName := fmt.Sprintf("gotosocial-%s", cfg.Host) | ||||||
| 	engine.Use(sessions.Sessions(sessionName, store)) | 	engine.Use(sessions.Sessions(sessionName, store)) | ||||||
| 	return nil | 	return nil | ||||||
|  |  | ||||||
							
								
								
									
										4
									
								
								mkdocs.yml
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										4
									
								
								mkdocs.yml
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,4 @@ | ||||||
|  | site_name: GoToSocial Documentation | ||||||
|  | theme: readthedocs | ||||||
|  | repo_url: https://github.com/superseriousbusiness/gotosocial | ||||||
|  | copyright: GoToSocial is licensed under the GNU AGPL v3 LICENSE. Copyright (C) 2021 the GoToSocial Authors. | ||||||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue