Fix credo issues

Signed-off-by: Thomas Citharel <tcit@tcit.fr>
This commit is contained in:
Thomas Citharel 2018-06-14 18:15:27 +02:00
parent ca36dd12e2
commit a007764d27
No known key found for this signature in database
GPG Key ID: A061B9DDE0CA0773
17 changed files with 267 additions and 44 deletions

160
.credo.exs Normal file
View File

@ -0,0 +1,160 @@
# This file contains the configuration for Credo and you are probably reading
# this after creating it with `mix credo.gen.config`.
#
# If you find anything wrong or unclear in this file, please report an
# issue on GitHub: https://github.com/rrrene/credo/issues
#
%{
#
# You can have as many configs as you like in the `configs:` field.
configs: [
%{
#
# Run any exec using `mix credo -C <name>`. If no exec name is given
# "default" is used.
#
name: "default",
#
# These are the files included in the analysis:
files: %{
#
# You can give explicit globs or simply directories.
# In the latter case `**/*.{ex,exs}` will be used.
#
included: ["lib/", "src/", "test/", "web/", "apps/"],
excluded: [~r"/_build/", ~r"/deps/"]
},
#
# If you create your own checks, you must specify the source files for
# them here, so they can be loaded by Credo before running the analysis.
#
requires: [],
#
# If you want to enforce a style guide and need a more traditional linting
# experience, you can change `strict` to `true` below:
#
strict: false,
#
# If you want to use uncolored output by default, you can change `color`
# to `false` below:
#
color: true,
#
# You can customize the parameters of any check by adding a second element
# to the tuple.
#
# To disable a check put `false` as second element:
#
# {Credo.Check.Design.DuplicatedCode, false}
#
checks: [
#
## Consistency Checks
#
{Credo.Check.Consistency.ExceptionNames},
{Credo.Check.Consistency.LineEndings},
{Credo.Check.Consistency.ParameterPatternMatching},
{Credo.Check.Consistency.SpaceAroundOperators},
{Credo.Check.Consistency.SpaceInParentheses},
{Credo.Check.Consistency.TabsOrSpaces},
#
## Design Checks
#
# You can customize the priority of any check
# Priority values are: `low, normal, high, higher`
#
{Credo.Check.Design.AliasUsage, priority: :low},
# For some checks, you can also set other parameters
#
# If you don't want the `setup` and `test` macro calls in ExUnit tests
# or the `schema` macro in Ecto schemas to trigger DuplicatedCode, just
# set the `excluded_macros` parameter to `[:schema, :setup, :test]`.
#
{Credo.Check.Design.DuplicatedCode, excluded_macros: []},
# You can also customize the exit_status of each check.
# If you don't want TODO comments to cause `mix credo` to fail, just
# set this value to 0 (zero).
#
{Credo.Check.Design.TagTODO, exit_status: 0},
{Credo.Check.Design.TagFIXME},
#
## Readability Checks
#
{Credo.Check.Readability.AliasOrder},
{Credo.Check.Readability.FunctionNames},
{Credo.Check.Readability.LargeNumbers},
{Credo.Check.Readability.MaxLineLength, priority: :low, max_length: 80},
{Credo.Check.Readability.ModuleAttributeNames},
{Credo.Check.Readability.ModuleDoc},
{Credo.Check.Readability.ModuleNames},
{Credo.Check.Readability.ParenthesesOnZeroArityDefs},
{Credo.Check.Readability.ParenthesesInCondition},
{Credo.Check.Readability.PredicateFunctionNames},
{Credo.Check.Readability.PreferImplicitTry},
{Credo.Check.Readability.RedundantBlankLines},
{Credo.Check.Readability.StringSigils},
{Credo.Check.Readability.TrailingBlankLine},
{Credo.Check.Readability.TrailingWhiteSpace},
{Credo.Check.Readability.VariableNames},
{Credo.Check.Readability.Semicolons},
{Credo.Check.Readability.SpaceAfterCommas},
#
## Refactoring Opportunities
#
{Credo.Check.Refactor.DoubleBooleanNegation},
{Credo.Check.Refactor.CondStatements},
{Credo.Check.Refactor.CyclomaticComplexity},
{Credo.Check.Refactor.FunctionArity},
{Credo.Check.Refactor.LongQuoteBlocks},
{Credo.Check.Refactor.MatchInCondition},
{Credo.Check.Refactor.NegatedConditionsInUnless},
{Credo.Check.Refactor.NegatedConditionsWithElse},
{Credo.Check.Refactor.Nesting, max_nesting: 3},
{Credo.Check.Refactor.PipeChainStart,
excluded_argument_types: [:atom, :binary, :fn, :keyword], excluded_functions: []},
{Credo.Check.Refactor.UnlessWithElse},
#
## Warnings
#
{Credo.Check.Warning.BoolOperationOnSameValues},
{Credo.Check.Warning.ExpensiveEmptyEnumCheck},
{Credo.Check.Warning.IExPry},
{Credo.Check.Warning.IoInspect},
{Credo.Check.Warning.LazyLogging},
{Credo.Check.Warning.OperationOnSameValues},
{Credo.Check.Warning.OperationWithConstantResult},
{Credo.Check.Warning.UnusedEnumOperation},
{Credo.Check.Warning.UnusedFileOperation},
{Credo.Check.Warning.UnusedKeywordOperation},
{Credo.Check.Warning.UnusedListOperation},
{Credo.Check.Warning.UnusedPathOperation},
{Credo.Check.Warning.UnusedRegexOperation},
{Credo.Check.Warning.UnusedStringOperation},
{Credo.Check.Warning.UnusedTupleOperation},
{Credo.Check.Warning.RaiseInsideRescue},
#
# Controversial and experimental checks (opt-in, just remove `, false`)
#
{Credo.Check.Refactor.ABCSize, false},
{Credo.Check.Refactor.AppendSingleItem, false},
{Credo.Check.Refactor.VariableRebinding, false},
{Credo.Check.Warning.MapGetUnsafePass, false},
{Credo.Check.Consistency.MultiAliasImportRequireUse, false},
#
# Deprecated checks (these will be deleted after a grace period)
#
{Credo.Check.Readability.Specs, false}
#
# Custom checks can be created using `mix credo.gen.check`.
#
]
}
]
}

View File

@ -335,9 +335,9 @@ defmodule Eventos.Actors do
Register user Register user
""" """
def register(%{email: email, password: password, username: username}) do def register(%{email: email, password: password, username: username}) do
key = :public_key.generate_key({:rsa, 2048, 65537}) key = :public_key.generate_key({:rsa, 2048, 65_537})
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key) entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
pem = :public_key.pem_encode([entry]) |> String.trim_trailing() pem = [entry] |> :public_key.pem_encode() |> String.trim_trailing()
import Exgravatar import Exgravatar
@ -375,9 +375,9 @@ defmodule Eventos.Actors do
end end
def register_bot_account(%{name: name, summary: summary}) do def register_bot_account(%{name: name, summary: summary}) do
key = :public_key.generate_key({:rsa, 2048, 65537}) key = :public_key.generate_key({:rsa, 2048, 65_537})
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key) entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
pem = :public_key.pem_encode([entry]) |> String.trim_trailing() pem = [entry] |> :public_key.pem_encode() |> String.trim_trailing()
actor = Eventos.Actors.Actor.registration_changeset(%Eventos.Actors.Actor{}, %{ actor = Eventos.Actors.Actor.registration_changeset(%Eventos.Actors.Actor{}, %{
preferred_username: name, preferred_username: name,

View File

@ -1,4 +1,8 @@
defmodule Eventos.Events.Comment do defmodule Eventos.Events.Comment do
@moduledoc """
An actor comment (for instance on an event or on a group)
"""
use Ecto.Schema use Ecto.Schema
import Ecto.Changeset import Ecto.Changeset

View File

@ -83,13 +83,13 @@ defmodule EventosWeb.ActivityPubController do
def inbox(conn, params) do def inbox(conn, params) do
headers = Enum.into(conn.req_headers, %{}) headers = Enum.into(conn.req_headers, %{})
if !String.contains?(headers["signature"] || "", params["actor"]) do if String.contains?(headers["signature"] || "", params["actor"]) do
Logger.info("Signature not from author, relayed message, fetching from source")
ActivityPub.fetch_event_from_url(params["object"]["id"])
else
Logger.info("Signature error") Logger.info("Signature error")
Logger.info("Could not validate #{params["actor"]}") Logger.info("Could not validate #{params["actor"]}")
Logger.info(inspect(conn.req_headers)) Logger.info(inspect(conn.req_headers))
else
Logger.info("Signature not from author, relayed message, fetching from source")
ActivityPub.fetch_event_from_url(params["object"]["id"])
end end
json(conn, "ok") json(conn, "ok")

View File

@ -60,7 +60,7 @@ defmodule EventosWeb.EventController do
end end
def export_to_ics(conn, %{"uuid" => uuid}) do def export_to_ics(conn, %{"uuid" => uuid}) do
event = Events.get_event_full_by_uuid(uuid) |> ICalendar.export_event() event = uuid |> Events.get_event_full_by_uuid() |> ICalendar.export_event()
send_resp(conn, 200, event) send_resp(conn, 200, event)
end end

View File

@ -1,4 +1,10 @@
defmodule EventosWeb.HTTPSignaturePlug do defmodule EventosWeb.HTTPSignaturePlug do
@moduledoc """
# HTTPSignaturePlug
Plug to check HTTP Signatures on every incoming request
"""
alias Eventos.Service.HTTPSignatures alias Eventos.Service.HTTPSignatures
import Plug.Conn import Plug.Conn
require Logger require Logger
@ -13,7 +19,9 @@ defmodule EventosWeb.HTTPSignaturePlug do
def call(conn, _opts) do def call(conn, _opts) do
user = conn.params["actor"] user = conn.params["actor"]
Logger.debug("Checking sig for #{user}") Logger.debug fn ->
"Checking sig for #{user}"
end
with [signature | _] <- get_req_header(conn, "signature") do with [signature | _] <- get_req_header(conn, "signature") do
cond do cond do
signature && String.contains?(signature, user) -> signature && String.contains?(signature, user) ->

View File

@ -49,9 +49,9 @@ defmodule EventosWeb.ActivityPub.ActorView do
end end
def render("following.json", %{actor: actor, page: page}) do def render("following.json", %{actor: actor, page: page}) do
following = Actor.get_followings(actor) actor
|> Actor.get_followings()
collection(following, actor.following_url, page) |> collection(actor.following_url, page)
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end
@ -68,9 +68,9 @@ defmodule EventosWeb.ActivityPub.ActorView do
end end
def render("followers.json", %{actor: actor, page: page}) do def render("followers.json", %{actor: actor, page: page}) do
followers = Actor.get_followers(actor) actor
|> Actor.get_followers()
collection(followers, actor.followers_url, page) |> collection(actor.followers_url, page)
|> Map.merge(Utils.make_json_ld_header()) |> Map.merge(Utils.make_json_ld_header())
end end

View File

@ -1,4 +1,8 @@
defmodule Mix.Tasks.CreateBot do defmodule Mix.Tasks.CreateBot do
@moduledoc """
Creates a bot from a source
"""
use Mix.Task use Mix.Task
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Actors.Bot alias Eventos.Actors.Bot

View File

@ -1,4 +1,10 @@
defmodule Eventos.Service.ActivityPub do defmodule Eventos.Service.ActivityPub do
@moduledoc """
# ActivityPub
Every ActivityPub method
"""
alias Eventos.Events alias Eventos.Events
alias Eventos.Events.{Event, Category} alias Eventos.Events.{Event, Category}
alias Eventos.Service.ActivityPub.Transmogrifier alias Eventos.Service.ActivityPub.Transmogrifier
@ -49,8 +55,8 @@ defmodule Eventos.Service.ActivityPub do
url, url,
[Accept: "application/activity+json"], [Accept: "application/activity+json"],
follow_redirect: true, follow_redirect: true,
timeout: 10000, timeout: 10_000,
recv_timeout: 20000 recv_timeout: 20_000
), ),
{:ok, data} <- Jason.decode(body), {:ok, data} <- Jason.decode(body),
nil <- Events.get_event_by_url!(data["id"]), nil <- Events.get_event_by_url!(data["id"]),
@ -285,9 +291,7 @@ defmodule Eventos.Service.ActivityPub do
case bot.type do case bot.type do
"ics" -> "ics" ->
{:ok, %HTTPoison.Response{body: body} = _resp} = HTTPoison.get(bot.source) {:ok, %HTTPoison.Response{body: body} = _resp} = HTTPoison.get(bot.source)
ical_events = body ical_events = body |> ExIcal.parse() |> ExIcal.by_range(DateTime.utc_now(), DateTime.utc_now() |> Timex.shift(years: 1))
|> ExIcal.parse()
|> ExIcal.by_range(DateTime.utc_now(), DateTime.utc_now() |> Timex.shift(years: 1))
activities = ical_events activities = ical_events
|> Enum.chunk_every(limit) |> Enum.chunk_every(limit)
|> Enum.at(page - 1) |> Enum.at(page - 1)

View File

@ -201,10 +201,10 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
if object = Object.get_by_ap_id(id), do: {:ok, object}, else: nil if object = Object.get_by_ap_id(id), do: {:ok, object}, else: nil
end end
def set_reply_to_uri(%{"inReplyTo" => inReplyTo} = object) do def set_reply_to_uri(%{"inReplyTo" => in_reply_to} = object) do
with false <- String.starts_with?(inReplyTo, "http"), with false <- String.starts_with?(in_reply_to, "http"),
{:ok, %{data: replied_to_object}} <- get_obj_helper(inReplyTo) do {:ok, %{data: replied_to_object}} <- get_obj_helper(in_reply_to) do
Map.put(object, "inReplyTo", replied_to_object["external_url"] || inReplyTo) Map.put(object, "inReplyTo", replied_to_object["external_url"] || in_reply_to)
else else
_e -> object _e -> object
end end
@ -332,10 +332,9 @@ defmodule Eventos.Service.ActivityPub.Transmogrifier do
# end # end
# #
def add_attributed_to(object) do def add_attributed_to(object) do
attributedTo = object["attributedTo"] || object["actor"] attributed_to = object["attributedTo"] || object["actor"]
object object |> Map.put("attributedTo", attributed_to)
|> Map.put("attributedTo", attributedTo)
end end
# #
# def prepare_attachments(object) do # def prepare_attachments(object) do

View File

@ -1,4 +1,10 @@
defmodule Eventos.Service.ActivityPub.Utils do defmodule Eventos.Service.ActivityPub.Utils do
@moduledoc """
# Utils
Various utils
"""
alias Eventos.Repo alias Eventos.Repo
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Actors.Actor alias Eventos.Actors.Actor

View File

@ -1,4 +1,8 @@
defmodule Eventos.Service.Federator do defmodule Eventos.Service.Federator do
@moduledoc """
Handle federated activities
"""
use GenServer use GenServer
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Activity alias Eventos.Activity
@ -16,7 +20,7 @@ defmodule Eventos.Service.Federator do
spawn(fn -> spawn(fn ->
# 1 minute # 1 minute
Process.sleep(1000 * 60 * 1) Process.sleep(1000 * 60)
end) end)
GenServer.start_link( GenServer.start_link(
@ -101,7 +105,9 @@ defmodule Eventos.Service.Federator do
end end
def handle_cast(m, state) do def handle_cast(m, state) do
IO.inspect("Unknown: #{inspect(m)}, #{inspect(state)}") Logger.error fn ->
"Unknown: #{inspect(m)}, #{inspect(state)}"
end
{:noreply, state} {:noreply, state}
end end

View File

@ -1,8 +1,14 @@
# https://tools.ietf.org/html/draft-cavage-http-signatures-08 # https://tools.ietf.org/html/draft-cavage-http-signatures-08
defmodule Eventos.Service.HTTPSignatures do defmodule Eventos.Service.HTTPSignatures do
@moduledoc """
# HTTP Signatures
Generates and checks HTTP Signatures
"""
alias Eventos.Actors.Actor alias Eventos.Actors.Actor
alias Eventos.Service.ActivityPub alias Eventos.Service.ActivityPub
require Logger import Logger
def split_signature(sig) do def split_signature(sig) do
default = %{"headers" => "date"} default = %{"headers" => "date"}
@ -22,8 +28,12 @@ defmodule Eventos.Service.HTTPSignatures do
def validate(headers, signature, public_key) do def validate(headers, signature, public_key) do
sigstring = build_signing_string(headers, signature["headers"]) sigstring = build_signing_string(headers, signature["headers"])
Logger.debug("Signature: #{signature["signature"]}") Logger.debug fn ->
Logger.debug("Sigstring: #{sigstring}") "Signature: #{signature["signature"]}"
end
Logger.debug fn ->
"Sigstring: #{sigstring}"
end
{:ok, sig} = Base.decode64(signature["signature"]) {:ok, sig} = Base.decode64(signature["signature"])
:public_key.verify(sigstring, :sha256, sig, public_key) :public_key.verify(sigstring, :sha256, sig, public_key)
end end
@ -74,14 +84,12 @@ defmodule Eventos.Service.HTTPSignatures do
with private_key = Actor.get_keys_for_actor(actor) do with private_key = Actor.get_keys_for_actor(actor) do
sigstring = build_signing_string(headers, Map.keys(headers)) sigstring = build_signing_string(headers, Map.keys(headers))
signature = signature = sigstring |> :public_key.sign(:sha256, private_key) |> Base.encode64()
:public_key.sign(sigstring, :sha256, private_key)
|> Base.encode64()
[ [
keyId: actor.url <> "#main-key", keyId: actor.url <> "#main-key",
algorithm: "rsa-sha256", algorithm: "rsa-sha256",
headers: Map.keys(headers) |> Enum.join(" "), headers: headers |> Map.keys() |> Enum.join(" "),
signature: signature signature: signature
] ]
|> Enum.map(fn {k, v} -> "#{k}=\"#{v}\"" end) |> Enum.map(fn {k, v} -> "#{k}=\"#{v}\"" end)

View File

@ -1,4 +1,10 @@
defmodule Eventos.Service.Streamer do defmodule Eventos.Service.Streamer do
@moduledoc """
# Streamer
Handles streaming activities
"""
use GenServer use GenServer
require Logger require Logger
alias Eventos.Accounts.Actor alias Eventos.Accounts.Actor
@ -30,7 +36,8 @@ defmodule Eventos.Service.Streamer do
end end
def handle_cast(%{action: :ping}, topics) do def handle_cast(%{action: :ping}, topics) do
Map.values(topics) topics
|> Map.values()
|> List.flatten() |> List.flatten()
|> Enum.each(fn socket -> |> Enum.each(fn socket ->
Logger.debug("Sending keepalive ping") Logger.debug("Sending keepalive ping")
@ -51,7 +58,9 @@ defmodule Eventos.Service.Streamer do
sockets_for_topic = sockets[topic] || [] sockets_for_topic = sockets[topic] || []
sockets_for_topic = Enum.uniq([socket | sockets_for_topic]) sockets_for_topic = Enum.uniq([socket | sockets_for_topic])
sockets = Map.put(sockets, topic, sockets_for_topic) sockets = Map.put(sockets, topic, sockets_for_topic)
Logger.debug("Got new conn for #{topic}") Logger.debug fn ->
"Got new conn for #{topic}"
end
{:noreply, sockets} {:noreply, sockets}
end end
@ -60,7 +69,9 @@ defmodule Eventos.Service.Streamer do
sockets_for_topic = sockets[topic] || [] sockets_for_topic = sockets[topic] || []
sockets_for_topic = List.delete(sockets_for_topic, socket) sockets_for_topic = List.delete(sockets_for_topic, socket)
sockets = Map.put(sockets, topic, sockets_for_topic) sockets = Map.put(sockets, topic, sockets_for_topic)
Logger.debug("Removed conn for #{topic}") Logger.debug fn ->
"Removed conn for #{topic}"
end
{:noreply, sockets} {:noreply, sockets}
end end

View File

@ -1,4 +1,9 @@
defmodule Eventos.Service.WebFinger do defmodule Eventos.Service.WebFinger do
@moduledoc """
# WebFinger
Performs the WebFinger requests and responses (json only)
"""
alias Eventos.Actors alias Eventos.Actors
alias Eventos.Service.XmlBuilder alias Eventos.Service.XmlBuilder
@ -59,7 +64,9 @@ defmodule Eventos.Service.WebFinger do
{"application/activity+json", "self"} -> {"application/activity+json", "self"} ->
Map.put(data, "url", link["href"]) Map.put(data, "url", link["href"])
_ -> _ ->
Logger.debug("Unhandled type: #{inspect(link["type"])}") Logger.debug fn ->
"Unhandled type: #{inspect(link["type"])}"
end
data data
end end
end) end)
@ -81,7 +88,7 @@ defmodule Eventos.Service.WebFinger do
address = "http://#{domain}/.well-known/webfinger?resource=acct:#{actor}" address = "http://#{domain}/.well-known/webfinger?resource=acct:#{actor}"
Logger.debug(inspect address) Logger.debug(inspect address)
with {:ok, %HTTPoison.Response{} = response} <- HTTPoison.get(address, [Accept: "application/json, application/activity+json, application/jrd+json"],follow_redirect: true), with {:ok, %HTTPoison.Response{} = response} <- HTTPoison.get(address, [Accept: "application/json, application/activity+json, application/jrd+json"], follow_redirect: true),
%{status_code: status_code, body: body} when status_code in 200..299 <- response do %{status_code: status_code, body: body} when status_code in 200..299 <- response do
{:ok, doc} = Jason.decode(body) {:ok, doc} = Jason.decode(body)
webfinger_from_json(doc) webfinger_from_json(doc)

View File

@ -1,4 +1,10 @@
defmodule Eventos.Service.XmlBuilder do defmodule Eventos.Service.XmlBuilder do
@moduledoc """
XML Builder.
Do we still need this ? Only for xrd ?
"""
def to_xml({tag, attributes, content}) do def to_xml({tag, attributes, content}) do
open_tag = make_open_tag(tag, attributes) open_tag = make_open_tag(tag, attributes)

View File

@ -15,9 +15,9 @@ defmodule Eventos.Factory do
end end
def actor_factory do def actor_factory do
key = :public_key.generate_key({:rsa, 2048, 65537}) key = :public_key.generate_key({:rsa, 2048, 65_537})
entry = :public_key.pem_entry_encode(:RSAPrivateKey, key) entry = :public_key.pem_entry_encode(:RSAPrivateKey, key)
pem = :public_key.pem_encode([entry]) |> String.trim_trailing() pem = [entry] |> :public_key.pem_encode() |> String.trim_trailing()
preferred_username = sequence("thomas") preferred_username = sequence("thomas")