Skip to content

Commit

Permalink
Add fields for basic auth (#1133)
Browse files Browse the repository at this point in the history
* Add fields for basic auth

* Use map instead of cycling through slice

* Format code

Co-authored-by: Julien Midedji <midedji@jacob.de>
  • Loading branch information
ResamVi and Julien Midedji authored Nov 2, 2021
1 parent 4f20013 commit 6070e64
Show file tree
Hide file tree
Showing 5 changed files with 189 additions and 9 deletions.
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ require (
github.com/lib/pq v1.2.0 // indirect
github.com/logrusorgru/aurora v0.0.0-20190803045625-94edacc10f9b
github.com/microsoft/azure-devops-go-api/azuredevops v0.0.0-20191014190507-26902c1d4325
github.com/mmcdole/gofeed v1.1.0
github.com/mmcdole/gofeed v1.1.4-0.20211013195857-68ee9054d97b
github.com/nicklaw5/helix v0.7.0
github.com/olebedev/config v0.0.0-20190528211619-364964f3a8e4
github.com/olekukonko/tablewriter v0.0.4
Expand Down
4 changes: 2 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -438,8 +438,8 @@ github.com/mitchellh/hashstructure v0.0.0-20170609045927-2bca23e0e452/go.mod h1:
github.com/mitchellh/mapstructure v1.1.2 h1:fmNYVwqnSfB9mZU6OS2O6GsXM+wcskZDuKQzvN1EDeE=
github.com/mitchellh/mapstructure v1.1.2/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
github.com/mitchellh/reflectwalk v1.0.0/go.mod h1:mSTlrgnPZtwu0c4WaC2kGObEpuNDbx0jmZXqmk4esnw=
github.com/mmcdole/gofeed v1.1.0 h1:T2WrGLVJRV04PY2qwhEJLHCt9JiCtBhb6SmC8ZvJH08=
github.com/mmcdole/gofeed v1.1.0/go.mod h1:PPiVwgDXLlz2N83KB4TrIim2lyYM5Zn7ZWH9Pi4oHUk=
github.com/mmcdole/gofeed v1.1.4-0.20211013195857-68ee9054d97b h1:l332QWu9K3Pc7SEe1iz4Sk4Q86/6vqgNLlVznagSyX0=
github.com/mmcdole/gofeed v1.1.4-0.20211013195857-68ee9054d97b/go.mod h1:QQO3maftbOu+hiVOGOZDRLymqGQCos4zxbA4j89gMrE=
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf h1:sWGE2v+hO0Nd4yFU/S/mDBM5plIU8v/Qhfz41hkDIAI=
github.com/mmcdole/goxpp v0.0.0-20181012175147-0068e33feabf/go.mod h1:pasqhqstspkosTneA62Nc+2p9SOBBYAPbnmRRWPQ0V8=
github.com/mndrix/tap-go v0.0.0-20170113192335-56cca451570b h1:/1WAr2jAZnfxlWW3M3VJ4P3VRi57AjbrUv9U1kCRHyk=
Expand Down
127 changes: 127 additions & 0 deletions modules/feedreader/auth_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,127 @@
package feedreader

import (
"net/http"
"testing"

"github.com/olebedev/config"
"gotest.tools/assert"
)

var listformat string = `
enabled: true
feeds:
- https://news.ycombinator.com/rss
- https://rss.cbc.ca/lineup/topstories.xml
feedLimit: 10
position:
top: 1
left: 1
width: 2
height: 1
refreshInterval: 14400`

var mapformat string = `
enabled: true
feeds:
http://localhost:3000/feed/rss.xml:
username: johndoe
password: supersecret
feedLimit: 10
position:
top: 1
left: 1
width: 2
height: 1
refreshInterval: 14400`

var globalConfig string = `
wtf:
colors:
border:
focusable: darkslateblue
focused: orange
normal: gray`

var sampleRSS string = `<?xml version="1.0" encoding="UTF-8" ?>
<rss version="2.0">
<channel>
<title>The Stoics</title>
<link>https://google.com</link>
<description>Testing feed to test private feeds</description>
<item>
<title>Marcus Aurelius</title>
<link>https://en.wikipedia.org/wiki/Marcus_Aurelius</link>
<description>
"The soul becomes dyed with the color of its thoughts."
</description>
</item>
<item>
<title>Seneca</title>
<link>https://en.wikipedia.org/wiki/Seneca_the_Younger</link>
<description>"We suffer more often in imagination than in reality"</description>
</item>
</channel>
</rss>
`

func TestMapFormatFetch(t *testing.T) {
ymlCfg, _ := config.ParseYaml(mapformat)
globalCfg, _ := config.ParseYaml(globalConfig)

settings := NewSettingsFromYAML("feedreader", ymlCfg, globalCfg)
widget := NewWidget(nil, nil, settings)

go setupPrivateRSSFeed()

f, err := widget.Fetch(widget.settings.feeds)
if err != nil {
t.Error(err)
}

assert.Equal(t, len(f), 2)
}

func TestListFormatFetch(t *testing.T) {
ymlCfg, _ := config.ParseYaml(listformat)
globalCfg, _ := config.ParseYaml(globalConfig)

settings := NewSettingsFromYAML("feedreader", ymlCfg, globalCfg)
widget := NewWidget(nil, nil, settings)

go setupPrivateRSSFeed()

f, err := widget.Fetch(widget.settings.feeds)
if err != nil {
t.Error(err)
}

if len(f) == 0 {
t.Error("No articles fetched")
}
}

func setupPrivateRSSFeed() {
http.HandleFunc("/feed/rss.xml", func(w http.ResponseWriter, r *http.Request) {
username, password, exists := r.BasicAuth()
if !exists || username != "johndoe" || password != "supersecret" {
http.Error(w, "Unauthorized", http.StatusUnauthorized)
return
}

w.Header().Set("Content-Type", "application/xml")

_, err := w.Write([]byte(sampleRSS))
if err != nil {
panic(err)
}
})
err := http.ListenAndServe(":3000", nil)
if err != nil {
panic(err)
}
}
46 changes: 41 additions & 5 deletions modules/feedreader/settings.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,21 +11,57 @@ const (
defaultTitle = "Feed Reader"
)

// auth stores [username, password]-credentials for private RSS feeds using Basic Auth
type auth struct {
username string
password string
}

// Settings defines the configuration properties for this module
type Settings struct {
*cfg.Common

feeds []string `help:"An array of RSS and Atom feed URLs"`
feedLimit int `help:"The maximum number of stories to display for each feed"`
feeds []string `help:"An array of RSS and Atom feed URLs"`
feedLimit int `help:"The maximum number of stories to display for each feed"`
credentials map[string]auth `help:"Map of private feed URLs with required authentication credentials"`
}

// NewSettingsFromYAML creates a new settings instance from a YAML config block
func NewSettingsFromYAML(name string, ymlConfig *config.Config, globalConfig *config.Config) *Settings {
settings := &Settings{
Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
Common: cfg.NewCommonSettingsFromModule(name, defaultTitle, defaultFocusable, ymlConfig, globalConfig),
feeds: utils.ToStrs(ymlConfig.UList("feeds")),
feedLimit: ymlConfig.UInt("feedLimit", -1),
credentials: make(map[string]auth),
}

// If feeds cannot be parsed as list try parsing as a map with username+password fields
if len(settings.feeds) == 0 {
credentials := make(map[string]auth)
feeds := make([]string, 0)
for url, creds := range ymlConfig.UMap("feeds") {
parsed, ok := creds.(map[string]interface{})
if !ok {
continue
}

user, ok := parsed["username"].(string)
if !ok {
continue
}
pass, ok := parsed["password"].(string)
if !ok {
continue
}

feeds: utils.ToStrs(ymlConfig.UList("feeds")),
feedLimit: ymlConfig.UInt("feedLimit", -1),
credentials[url] = auth{
username: user,
password: pass,
}
feeds = append(feeds, url)
}
settings.feeds = feeds
settings.credentials = credentials
}

return settings
Expand Down
19 changes: 18 additions & 1 deletion modules/feedreader/widget.go
Original file line number Diff line number Diff line change
Expand Up @@ -125,7 +125,24 @@ func (widget *Widget) Render() {
/* -------------------- Unexported Functions -------------------- */

func (widget *Widget) fetchForFeed(feedURL string) ([]*FeedItem, error) {
feed, err := widget.parser.ParseURL(feedURL)
var (
feed *gofeed.Feed
err error
)
if auth, isPrivateRSS := widget.settings.credentials[feedURL]; isPrivateRSS {
fp := gofeed.NewParser()
fp.AuthConfig = &gofeed.Auth{
Username: auth.username,
Password: auth.password,
}
feed, err = fp.ParseURL(feedURL)
} else {
feed, err = widget.parser.ParseURL(feedURL)
}
if err != nil {
return nil, err
}

if err != nil {
return nil, err
}
Expand Down

0 comments on commit 6070e64

Please sign in to comment.