DEV Community

Adam.S
Adam.S

Posted on • Edited on • Originally published at bas-man.dev

2 2

Swift: Deconstruct SPF: Qualifier and MechanismKind

In this article I will be looking at defining the enums for Qualifier and MechanismKind. I will also be adding tests to ensure that the functions work as expected. Further I will implement a simple asMechanism() as a proof of concept.

Qualifier

As mentioned in an earlier article. The Qualifier will consist of the following values.

  • + Pass
  • - Fail
  • ~ SoftFail
  • ? Neutral
  • None

I expect that I will want to be able to obtain the character value (rawValue)
at some point in the future.

Definition

enum Qualifier: String {
    case Pass
    case Fail
    case SoftFail
    case Neutral
    case None

    func get() -> String {
        switch self {
        case .Pass:
            return "+"
        case .Fail:
            return "-"
        case .SoftFail:
            return "~"
        case .Neutral:
            return "?"
        case .None:
            return ""
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

At this point I have diverged from the original definition. This gave me a chance to look at the switch statement in Swift. I think will change this in a refactor session, as I expect to be able to access the Raw Value. Scroll down to find the actual documentation.

Testing

To make testing a little cleaner and manageable moving forward. I also created a new file under DeconfSpfTests called DeconfSpfQualifierTests.swift.

This contains the following code.

import XCTest
@testable import DeconSpf;

final class SPFQualifierTests: XCTestCase {

    func testQualifierPass() {
        let Q = Qualifier.Pass;
        XCTAssertEqual(Q.get(), "+");
    }
    func testQualifierFail() {
        let Q = Qualifier.Fail;
        XCTAssertEqual(Q.get(), "-");
    }
    func testQualifierSoftFail() {
        let Q = Qualifier.SoftFail;
        XCTAssertEqual(Q.get(), "~");
    }
    func testQualifierNeutral() {
        let Q = Qualifier.Neutral;
        XCTAssertEqual(Q.get(), "?");
    }
    func testQualifierNone() {
        let Q = Qualifier.None;
        XCTAssertEqual(Q.get(), "");
    }

    static var allTests = [
        ("testQualifierPass", testQualifierPass),
        ("testQualifierFail", testQualifierFail),
        ("testQualifierSoftFail", testQualifierSoftFail),
        ("testQualifierNeutral", testQualifierNeutral),
        ("testQualifierNone", testQualifierNone),

    ]
}
Enter fullscreen mode Exit fullscreen mode
  • Note: The class name needs to be unique
    • final class SPFQualifierTests: XCTestCase
    • I am using SPFQualifierTests

MechanismKind

I took the same approach here with MechanismKind.

Definition

enum MechanismKind {
    case Redirect, Include, A, MX, ip4, ip6, All;

    func get() -> String {
        switch self {
        case .Redirect:
            return "redirect="
        case .Include:
            return "include:"
        case .A:
            return "a"
        case .MX:
            return "mx"
        case .ip4:
            return "ip4:"
        case .ip6:
            return "ip6:"
        case .All:
            return "all"
        }
    }
}

Enter fullscreen mode Exit fullscreen mode

Testing

I also again created a new test file called DeconfSpfMechanismKindTests.

This also has a unique class name SPFMechanimsKindTests.

import XCTest
@testable import DeconSpf;

final class SPFMechanimsKindTests: XCTestCase {

    func testMechanimsKindRedirect() {
        let MK = MechanismKind.Redirect;
        XCTAssertEqual(MK.get(), "redirect=");
    }
    func testMechanimsKindInclude() {
        let MK = MechanismKind.Include;
        XCTAssertEqual(MK.get(), "include:");
    }
    func testMechanimsKindA() {
        let MK = MechanismKind.A;
        XCTAssertEqual(MK.get(), "a");
    }
    func testMechanimsKindMX() {
        let MK = MechanismKind.MX;
        XCTAssertEqual(MK.get(), "mx");
    }
    func testMechanimsKindIp4() {
        let MK = MechanismKind.ip4;
        XCTAssertEqual(MK.get(), "ip4:");
    }
    func testMechanimsKindIp6() {
        let MK = MechanismKind.ip6;
        XCTAssertEqual(MK.get(), "ip6:");
    }
    func testMechanimsKindAll() {
        let MK = MechanismKind.All;
        XCTAssertEqual(MK.get(), "all");
    }

    static var allTests = [
        ("testMechanismKindRedirect", testMechanimsKindRedirect),
        ("testMechanismKindInclude", testMechanimsKindInclude),
        ("testMechanismKindA", testMechanimsKindA),
        ("testMechanismKindMX", testMechanimsKindMX),
        ("testMechanismKindIp4", testMechanimsKindIp4),
        ("testMechanismKindIp6", testMechanimsKindIp6),
        ("testMechanismKindAll", testMechanimsKindAll),

    ]
}
Enter fullscreen mode Exit fullscreen mode

asMechanism

Here I would refer you to the documentation on Strings and Characters.

Code

I have updated struct Mechanism as shown below

struct Mechanims {
  --snip--
  func asMechanism() -> String {
      var mechanismString = String();
      // TODO: Qualifier
      // Access the string for this mechanism's kind.
      mechanismString += self.kind.get();
      // Access the string for the mechanim
      mechanismString += self.mechanism;
      return mechanismString;
  }
  --snip--
}
Enter fullscreen mode Exit fullscreen mode

Tests

I now have my previous test working as well as a few new ones.

func testAsMechanismIp4() {
    let Mech = Mechanism(k: MechanismKind.ip4, q: Qualifier.None, m: "192.168.1.0/24");
    XCTAssertEqual(Mech.asMechanism(), "ip4:192.168.1.0/24");
}
func testAsMechanismIp6() {
    let Mech = Mechanism(k: MechanismKind.ip6, q: Qualifier.None, m: "X:X:X:X/16");
    XCTAssertEqual(Mech.asMechanism(), "ip6:X:X:X:X/16");
}
func testAsMechanismRedirect() {
    let Mech = Mechanism(k: MechanismKind.Redirect, q: Qualifier.None, m: "_spf.example.com");
    XCTAssertEqual(Mech.asMechanism(), "redirect=_spf.example.com");
}
func testAsMechanismInclude() {
    let Mech = Mechanism(k: MechanismKind.Include, q: Qualifier.None, m: "_spf1.example.com");
    XCTAssertEqual(Mech.asMechanism(), "include:_spf1.example.com");
}
func testAsMechanismA() {
    let Mech = Mechanism(k: MechanismKind.A, q: Qualifier.None, m: "");
    XCTAssertEqual(Mech.asMechanism(), "a");
}
func testAsMechanismMx() {
    let Mech = Mechanism(k: MechanismKind.MX, q: Qualifier.None, m: "");
    XCTAssertEqual(Mech.asMechanism(), "mx");
}
Enter fullscreen mode Exit fullscreen mode

Note that these tests are not complete. And do not yet cover all cases. In particular these tests will fail with several examples of A and MX Mechanisms.

The code for this project can now be found here.

This is the diff between Initial and Initial-asMechanism.

Closing

Today I implemented an initial get() function for both Qualifier and MechanismKind. I also did an initial implementation for asMechanism().

AWS Security LIVE!

Tune in for AWS Security LIVE!

Join AWS Security LIVE! for expert insights and actionable tips to protect your organization and keep security teams prepared.

Learn More

Top comments (0)

Billboard image

The Next Generation Developer Platform

Coherence is the first Platform-as-a-Service you can control. Unlike "black-box" platforms that are opinionated about the infra you can deploy, Coherence is powered by CNC, the open-source IaC framework, which offers limitless customization.

Learn more