mirror of
				https://github.com/owncast/owncast.git
				synced 2025-10-31 18:18:06 +08:00 
			
		
		
		
	Disconnect stream Admin API + HTTP Basic Auth (#204)
* Create http auth middleware * Add support for ending the inbound stream. Closes #191 * Add a simple success response to API requests
This commit is contained in:
		
							
								
								
									
										19
									
								
								controllers/admin.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										19
									
								
								controllers/admin.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,19 @@ | |||||||
|  | package controllers | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/gabek/owncast/core" | ||||||
|  | 	"github.com/gabek/owncast/core/rtmp" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // DisconnectInboundConnection will force-disconnect an inbound stream | ||||||
|  | func DisconnectInboundConnection(w http.ResponseWriter, r *http.Request) { | ||||||
|  | 	if !core.GetStatus().Online { | ||||||
|  | 		writeSimpleResponse(w, false, "no inbound stream connected") | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	rtmp.Disconnect() | ||||||
|  | 	writeSimpleResponse(w, true, "inbound stream disconnected") | ||||||
|  | } | ||||||
| @ -3,6 +3,8 @@ package controllers | |||||||
| import ( | import ( | ||||||
| 	"encoding/json" | 	"encoding/json" | ||||||
| 	"net/http" | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/gabek/owncast/models" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| type j map[string]interface{} | type j map[string]interface{} | ||||||
| @ -24,3 +26,12 @@ func badRequestHandler(w http.ResponseWriter, err error) { | |||||||
| 	w.WriteHeader(http.StatusBadRequest) | 	w.WriteHeader(http.StatusBadRequest) | ||||||
| 	json.NewEncoder(w).Encode(j{"error": err.Error()}) | 	json.NewEncoder(w).Encode(j{"error": err.Error()}) | ||||||
| } | } | ||||||
|  |  | ||||||
|  | func writeSimpleResponse(w http.ResponseWriter, success bool, message string) { | ||||||
|  | 	response := models.BaseAPIResponse{ | ||||||
|  | 		Success: success, | ||||||
|  | 		Message: message, | ||||||
|  | 	} | ||||||
|  | 	w.WriteHeader(http.StatusOK) | ||||||
|  | 	json.NewEncoder(w).Encode(response) | ||||||
|  | } | ||||||
|  | |||||||
| @ -28,6 +28,7 @@ var ( | |||||||
|  |  | ||||||
| var _transcoder ffmpeg.Transcoder | var _transcoder ffmpeg.Transcoder | ||||||
| var _pipe *os.File | var _pipe *os.File | ||||||
|  | var _rtmpConnection net.Conn | ||||||
|  |  | ||||||
| //Start starts the rtmp service, listening on port 1935 | //Start starts the rtmp service, listening on port 1935 | ||||||
| func Start() { | func Start() { | ||||||
| @ -92,6 +93,7 @@ func HandleConn(c *rtmp.Conn, nc net.Conn) { | |||||||
|  |  | ||||||
| 	_isConnected = true | 	_isConnected = true | ||||||
| 	core.SetStreamAsConnected() | 	core.SetStreamAsConnected() | ||||||
|  | 	_rtmpConnection = nc | ||||||
|  |  | ||||||
| 	f, err := os.OpenFile(pipePath, os.O_RDWR, os.ModeNamedPipe) | 	f, err := os.OpenFile(pipePath, os.O_RDWR, os.ModeNamedPipe) | ||||||
| 	_pipe = f | 	_pipe = f | ||||||
| @ -121,9 +123,20 @@ func handleDisconnect(conn net.Conn) { | |||||||
| 	_pipe.Close() | 	_pipe.Close() | ||||||
| 	_isConnected = false | 	_isConnected = false | ||||||
| 	_transcoder.Stop() | 	_transcoder.Stop() | ||||||
|  | 	_rtmpConnection = nil | ||||||
| 	core.SetStreamAsDisconnected() | 	core.SetStreamAsDisconnected() | ||||||
| } | } | ||||||
|  |  | ||||||
|  | // Disconnect will force disconnect the current inbound RTMP connection. | ||||||
|  | func Disconnect() { | ||||||
|  | 	if _rtmpConnection == nil { | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  |  | ||||||
|  | 	log.Infoln("Inbound stream disconnect requested.") | ||||||
|  | 	handleDisconnect(_rtmpConnection) | ||||||
|  | } | ||||||
|  |  | ||||||
| //IsConnected gets whether there is an rtmp connection or not | //IsConnected gets whether there is an rtmp connection or not | ||||||
| //this is only a getter since it is controlled by the rtmp handler | //this is only a getter since it is controlled by the rtmp handler | ||||||
| func IsConnected() bool { | func IsConnected() bool { | ||||||
|  | |||||||
							
								
								
									
										7
									
								
								models/baseAPIResponse.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								models/baseAPIResponse.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,7 @@ | |||||||
|  | package models | ||||||
|  |  | ||||||
|  | // BaseAPIResponse is a simple response to API requests. | ||||||
|  | type BaseAPIResponse struct { | ||||||
|  | 	Success bool   `json:"success"` | ||||||
|  | 	Message string `json:"message"` | ||||||
|  | } | ||||||
							
								
								
									
										34
									
								
								router/middleware/auth.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										34
									
								
								router/middleware/auth.go
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,34 @@ | |||||||
|  | package middleware | ||||||
|  |  | ||||||
|  | import ( | ||||||
|  | 	"crypto/subtle" | ||||||
|  | 	"net/http" | ||||||
|  |  | ||||||
|  | 	"github.com/gabek/owncast/config" | ||||||
|  | 	log "github.com/sirupsen/logrus" | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | // RequireAdminAuth wraps a handler requiring HTTP basic auth for it using the given | ||||||
|  | // the stream key as the password and and a hardcoded "admin" for username. | ||||||
|  | func RequireAdminAuth(handler http.HandlerFunc) http.HandlerFunc { | ||||||
|  | 	username := "admin" | ||||||
|  | 	password := config.Config.VideoSettings.StreamingKey | ||||||
|  |  | ||||||
|  | 	return func(w http.ResponseWriter, r *http.Request) { | ||||||
|  |  | ||||||
|  | 		user, pass, ok := r.BasicAuth() | ||||||
|  | 		realm := "Owncast Authenticated Request" | ||||||
|  |  | ||||||
|  | 		// Failed | ||||||
|  | 		if !ok || subtle.ConstantTimeCompare([]byte(user), []byte(username)) != 1 || subtle.ConstantTimeCompare([]byte(pass), []byte(password)) != 1 { | ||||||
|  | 			w.Header().Set("WWW-Authenticate", `Basic realm="`+realm+`"`) | ||||||
|  | 			http.Error(w, "Unauthorized", http.StatusUnauthorized) | ||||||
|  | 			log.Warnln("Failed authentication for", r.URL.Path, "from", r.RemoteAddr, r.UserAgent()) | ||||||
|  | 			return | ||||||
|  | 		} | ||||||
|  |  | ||||||
|  | 		// Success | ||||||
|  | 		log.Traceln("Authenticated request OK for", r.URL.Path, "from", r.RemoteAddr, r.UserAgent()) | ||||||
|  | 		handler(w, r) | ||||||
|  | 	} | ||||||
|  | } | ||||||
| @ -10,6 +10,7 @@ import ( | |||||||
| 	"github.com/gabek/owncast/controllers" | 	"github.com/gabek/owncast/controllers" | ||||||
| 	"github.com/gabek/owncast/core/chat" | 	"github.com/gabek/owncast/core/chat" | ||||||
| 	"github.com/gabek/owncast/core/rtmp" | 	"github.com/gabek/owncast/core/rtmp" | ||||||
|  | 	"github.com/gabek/owncast/router/middleware" | ||||||
| ) | ) | ||||||
|  |  | ||||||
| //Start starts the router for the http, ws, and rtmp | //Start starts the router for the http, ws, and rtmp | ||||||
| @ -43,6 +44,11 @@ func Start() error { | |||||||
| 		http.HandleFunc("/embed/video", controllers.GetVideoEmbed) | 		http.HandleFunc("/embed/video", controllers.GetVideoEmbed) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | 	// Authenticated admin requests | ||||||
|  |  | ||||||
|  | 	// Disconnect inbound stream | ||||||
|  | 	http.HandleFunc("/api/admin/disconnect", middleware.RequireAdminAuth(controllers.DisconnectInboundConnection)) | ||||||
|  |  | ||||||
| 	port := config.Config.GetPublicWebServerPort() | 	port := config.Config.GetPublicWebServerPort() | ||||||
|  |  | ||||||
| 	log.Infof("Web server running on port: %d", port) | 	log.Infof("Web server running on port: %d", port) | ||||||
|  | |||||||
		Reference in New Issue
	
	Block a user
	 Gabe Kangas
					Gabe Kangas